/* Quick Guide

  If you want drum maps for Battery 3 or Battery 4, they are included here:

  http://stash.reaper.fm/v/18598/sequencer_baby_v2_jnif.zip

- Right click/drag: Audition existing note or empty location.
  Sets velocity for new notes if existing note is auditioned.
  
- Left click/drag: Freehand draw or erase notes in piano mode.  
- Left click/drag: Draw or erase notes on the same row in drum mode.
- Shift + Ctrl + Alt + Left click/drag: Freehand draw or erase notes in drum mode.  
- Ctrl + Left click/drag: Edit velocity of notes.
  Drag over multiple notes on the same row to create velocity curves.
- Ctrl + Alt + Left click/drag: Adjust velocity of all notes on the same row.
- Shift + Left click/drag left/right: Adjust note start offset.
- Shift + Alt + Left click/drag: Adjust note start offset of all notes on the same row.
- Alt + Left click: Tie/untie notes.
- Alt + Left drag: Tie/untie multiple notes in a row.
- Shift + Right click drag up/down: Subdivide note.

- Left click drag up/down note names in Drum map mode: Transpose note row.
- Left click drag up/down MIDI channel number in Drum map mode: Change MIDI channel of note row.
- Shift + Left click piano keys / note names: Duplicate sequence. Sequence length is doubled.
- Ctrl + Left click piano keys / note names: Halve steps per beat, slow down
- Ctrl + Right click piano keys / note names: Double steps per beat, speed up
- Ctrl + Alt + Left click piano keys / note names: Halve steps per beat, preserve note positions. Sequence length is halved. 
- Ctrl + Alt + Right click piano keys / note names: Double steps per beat, preserve note positions. Sequence length is doubled.

- Left click drag green area on toolbar pianokeys: Change base note of the grid.
- Right click drag green area on toolbar pianokeys: Change number of notes in the grid.
- Left click drag red area on toolbar pianokeys: Change MIDI trigger notes for pattern triggering.

- Left click pattern button: Change pattern. 
- Ctrl + Left click pattern button: Copy the active pattern to clicked pattern and change to the clicked pattern. 
- Ctrl + Right click pattern button: Clear pattern.
- Alt + Left click pattern button: Set pattern chain end (Alt + Left Click Pattern 1/Last Pattern to clear)

- Left click Mode button (PR=Piano Roll, DM=Drum Map): Change mode.
- Left click "Play before start" button: Enable/Disable playback before start beat position.
  ">|>" = play everywhere
  " |>" = play only after start beat position
- Left click "Start beat position" value: Set start beat position to current play cursor position.
- Left click "End beat position" value: Set end beat position to current play cursor position. "---" = play infinitely.
- Right click "Start beat position" value: Set start beat position to default value, "0.000".
- Right click "End beat position" value: Set end beat position to default value, "---".
- Left click/drag "Note length" slider: Set/adjust note length for all notes. 100% equals full step length.
- Left click/drag Swing slider: Set/adjust swing.

- Right Click sliders to step through common values.
- Left/Right Click color scheme name to change colors.
- Left/Right Click midi mode to change.

- Move function:
  - Left / Right Click Move Button - Move Left / Right .
  - Add SHIFT - Move Up, Down.
  - Add CTRL for single step per click.
  - Add ALT to Left / Right move to lock CC's.

- Left click/drag on envelope lane: Draw/adjust envelope of the active envelope type.
- Right click/drag on envelope lane: Erase envelope of the active envelope type.
- Left click drag up/down envelope MIDI channel (in Drum map mode): Change MIDI channel of envelope.
- Left click drag up/down envelope type names/numbers: Change envelope type.
  Types:
  MIDI CC 0-127. 
  On first envelope (the blue one) type 127 has a special meaning.
  It controls probability of note playback on each sequence step.
  Only notes on same channel as the envelope will be affected.

Under 'settings' dropdown you can select 96 pattern, 64 step mode or 48 pattern, 128 step mode. THIS CLEARS ALL CURRENT PATTERNS. Save First if you have done any work!  
*/

desc:sequencer megababy - awesome sequencer v3/jnif - nandy's mod
slider1:0<0,95,1{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96}>--Pattern
slider2:36<0,127,1>--Note start
slider3:16<1,128,1>--Sequence length
slider4:25<1,49,1>--Number of notes
slider5:1<0.125,4.0,.125>--Rate
slider6:100<1,100,1>--Note length
slider7:0<0,1,1{Piano roll,Drum map}>--Mode
slider8:0<0,100,1>--Swing
slider9:4<1,16,1>--Steps per beat
slider10:0<0,6,1{Midi Off,Pattern Change,Pattern Change + Transpose,Pattern Change + Resync,Pattern change + Transpose + Resync,Pattern Change + Resync Quantized,Pattern Change + Transpose + Resync Quantized)}>--MIDI Trigger
slider11:96<0,127,1>--Trigger note start
slider12:0<0,23,1{off,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23}>--Chain
slider13:0.15<0.0,1.0,0.001>--Lane height percent
slider14:0<0,3,1>--CC to adjust (active for editing)
slider15:/seqbaby_data:_Default Kit.txt:Drum Map note names
slider20:1<0,127,1>--Controller0 type
slider21:7<0,127,1>--Controller1 type
slider22:10<0,127,1>--Controller2 type
slider23:11<0,127,1>--Controller3 type
slider30:0<0,15,0>--Controller0 channel
slider31:0<0,15,0>--Controller1 channel
slider32:0<0,15,0>--Controller2 channel
slider33:0<0,15,0>--Controller3 channel
slider40:0<-99,9999,1>--Start beat position
slider41:1<0,1,1>--Play before start
slider42:-99<-99,9999,1>--End beat position
slider43:5<1,9,1>--Colorscheme
slider44:0<0,1,1>--PatShift
slider45:0<0,1,1>--PatternMode
slider46:0<0,1,1>--XtraPatts
slider47:48<48,96,48>--NumberOfPatts
in_pin:none
out_pin:none

@init

//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////

octave_offset=-1; // Set this value as you prefer.

// It works the same way as "MIDI octave name display offset" in Reaper's preferences
//  1 : MIDI note 60 = C5
//  0 : MIDI note 60 = C4 
// -1 : MIDI note 60 = C3  ...

//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////

function setcolor(r,g,b,a) (gfx_r=r/255; gfx_g=g/255; gfx_b=b/255; gfx_a=a/255;);
function color(n) (  

  n == 4  ? (setcolor(255,255,255,255);); // button txt white
  n == 7  ? (setcolor(0,0,0,255););       // black
  n == 8  ? (setcolor(41,41,41,255););    // drum mode white key background
  n == 11 ? (setcolor(255,0,0,205););     // dm midi xpose, red
  n == 12 ? (setcolor(63,127,63,255););   // green keyzone 1
  n == 13 ? (setcolor(127,63,63,255););   // red keyzone 1
  n == 14 ? (setcolor(127,255,127,255);); // green keyzone 2
  n == 15 ? (setcolor(255,127,127,255);); // red keyzone 2

  colorscheme <5 ? (
    n == 1  ? (setcolor(179,179,179,255););  // button bg colour
    n == 2  ? (setcolor(51,102,255,102););   // button hilite
    n == 3  ? (setcolor(153,229,153,255););  // button chain ptn  
    n == 5  ? (setcolor(77,77,77,255););     // button txt pttn used
    n == 6  ? (setcolor(141,141,141,255););  // button txt pttn empty
    n == 9  ? (setcolor(255,255,255,153););  // dm midi ch num txt, see thru white
    n == 10 ? (setcolor(0,0,127,255););      // dm midi ch num bg, blue
    n == 16 ? (setcolor(0,0,0,180););        // see thru black
    n == 17 ? (setcolor(255,0,0,120););      // see thru red
    n == 18 ? (setcolor(41,61,104,255););    // octave line
    n == 19 ? (setcolor(51,76,127,255););    // barline
    n == 22 ? (setcolor(204,204,204,255););  // time position selected
    n == 23 ? (setcolor(102,102,102,255););  // time position not sel
    n == 26 ? (setcolor(127,127,127,255););  // text for vel/shift - light
    n == 31 ? (setcolor(0,0,0,255););        // BG
    n == 32 ? (setcolor(154,154,174,255););  // Divider Top
    n == 33 ? (setcolor(129,129,149,255););  // Divider Center
    n == 34 ? (setcolor(77,77,97,255););     // Divider Bottom (hehe...bottom...! :)
    n == 35 ? (setcolor(255,255,255,255););  // Env Label
    n == 36 ? (setcolor(255,255,255,153););  // velobar, see thru white
    n == 37 ? (setcolor(77,77,77,255););     // button txt labels dark
    n == 38 ? (setcolor(220,220,220,255););  // button txt labels light
    colorscheme <3 ? (
      colorscheme == 1 ? (
        colorschemename="Original";
        n == 27 ? (setcolor(51,76,255,255););  // Env 1
        n == 28 ? (setcolor(51,152,51,255););  // Env 2
        n == 29 ? (setcolor(255,127,0,255););  // Env 3
        n == 30 ? (setcolor(152,51,152,255);); // Env 4
      );  
      colorscheme == 2 ? (
        colorschemename="Redshift";
        n == 20 ? (setcolor(152,51,51,255););            // selected first beat  
        n == 21 ? (setcolor(51,51,152,255););            // selected other beats 
        n == 24 ? (setcolor(84,28,28,255););             // grid squares fist beat
        n == 25 ? (setcolor(28,28,84,255););             // grid squares other beats
        n == 27 || n == 29 ? (setcolor(162,28,28,255);); // Env 1,3
        n == 28 || n == 30? (setcolor(28,28,162,255););  // Env 2,4
      );
    );
    colorscheme == 3 ? (
    colorschemename="Half Life";
      n == 20 ? (setcolor(152,51,152,255););   // selected first beat  
      n == 21 ? (setcolor(51,152,152,255););   // selected other beats 
      n == 24 ? (setcolor(84,28,84,255););     // grid squares fist beat
      n == 25 ? (setcolor(28,84,84,255););     // grid squares other beats
      n == 27 || n == 29 ? (setcolor(162,28,162,255););  // Env 1,3
      n == 28 || n == 30? (setcolor(28,162,162,255););  // Env 2,4
    );
    colorscheme == 4 ? (
      colorschemename="Dark Daze";
      n == 20 ? (setcolor(51,51,102,255););     // selected first beat  
      n == 21 ? (setcolor(51,102,51,255););     // selected other beats 
      n == 24 ? (setcolor(28,28,56,255););     // grid squares fist beat
      n == 25 ? (setcolor(28,56,28,255););     // grid squares other beats
      n == 27 || n == 29 ? (setcolor(28,28,162,255););  // Env 1,3
      n == 28 || n == 30? (setcolor(28,108,28,255););  // Env 2,4
    );
  );
  colorscheme == 5 || colorscheme == 6 ? (
    n == 5 ? (setcolor(0,0,0,255););        // button txt pttn used, labels
    n == 6 ? (setcolor(75,95,123,255););    // button txt pttn empty
    n == 6 ? (setcolor(95,95,113,255););    // button txt pttn empty
    n == 8 ? (setcolor(41,41,41,255););     // drum mode white key background
    n == 9 ? (setcolor(220,220,220,127););  // dm midi ch num txt, see thru black
    n == 10 ? (setcolor(37,37,37,255););    // dm midi ch num bg
    n == 11 ? (setcolor(255,0,0,205););      // dm midi xpose, red
    n == 16 ? (setcolor(0,0,0,180););      // see thru black
    n == 17 ? (setcolor(255,0,0,120););      // see thru red
    n == 18 ? (setcolor(100,100,100,127);); // octave line
    n == 19 ? (setcolor(100,100,100,255);); // barline
    n == 31 ? (setcolor(84,84,84,255););    // BG
    n == 35 ? (setcolor(255,255,255,255);); // Env Label
    n == 36 ? (setcolor(0,0,0,127););        // velobar, see thru black
    n == 37 ? (setcolor(80,80,80,255););    // button txt labels dark
    n == 38 ? (setcolor(210,210,210,255);); // button txt labels light
    colorscheme == 5 ? (
      colorschemename="BlueMeanie";
      n == 1 ? (setcolor(130,145,170,255 ););   // button bg colour
      n == 2 ? (setcolor(150,195,255,102););   // button hilite
      n == 3 ? (setcolor(157,177,206,255););   // button chain ptn
      n == 20 ? (setcolor(100,130,184,255););  // selected first beat
      n == 21 ? (setcolor(130,160,214,255););  // selected other beats
      n == 22 ? (setcolor(209,224,239,255););  // time position selected
      n == 23 ? (setcolor(115,130,155,255););  // time position not sel
      n == 24 ? (setcolor(82,87,93,255););  // grid squares first beat
      n == 25 ? (setcolor(111,114,120,255););  // grid squares other beats
      n == 26 ? (setcolor(195,195,195,255););  // text for vel/shift
      n > 26 && n < 31 ? (setcolor(100,130,220,255););  // Env 1-4
      n == 32 ? (setcolor(164,174,185,255););  // Divider Top
      n == 33 ? (setcolor(139,149,160,255););  // Divider Center
      n == 34 ? (setcolor(87,97,105,255););      // Divider Bottom
    ) : (
      colorschemename="Subzero";
      n == 1 ? (setcolor(110,138,166,255););   // button bg colour
      n == 2 ? (setcolor(146,195,246,102););   // button hilite
      n == 3 ? (setcolor(146,195,246,255););   // button chain ptn
      n == 20 ? (setcolor(179,194,209,255););  // selected first beat
      n == 21 ? (setcolor(209,224,239,255););  // selected other beats
      n == 22 ? (setcolor(229,244,255,255););  // time position selected
      n == 23 ? (setcolor(110,138,166,255););  // time position not sel
      n == 24 ? (setcolor(91,97,103,255););     // grid squares first beat
      n == 25 ? (setcolor(121,127,133,255););  // grid squares other beats
      n == 26 ? (setcolor(165,165,165,255););  // text for vel/shift
      n == 27 ? (setcolor(100,130,184,255););  // Env 1
      n == 28 ? (setcolor(100,130,194,255););  // Env 2
      n == 29 ? (setcolor(100,130,204,255););  // Env 3
      n == 30 ? (setcolor(100,130,214,255););  // Env 4  
      n == 32 ? (setcolor(174,184,195,255););  // Divider Top
      n == 33 ? (setcolor(149,159,170,255););  // Divider Center
      n == 34 ? (setcolor(97,107,115,255););  // Divider Bottom
    );
  );
  colorscheme == 7 || colorscheme == 8 ? (  
  n == 5 ? (setcolor(0,0,0,255););        // button txt pttn used, labels
  n == 6 ? (setcolor(140,140,140,255););   // button txt pttn empty
  n == 8 ? (setcolor(41,41,41,255););   // drum mode white key background
  n == 9 ? (setcolor(220,220,220,127););  // dm midi ch num txt, see thru black  
  n == 11 ? (setcolor(255,0,0,205););  // dm midi xpose, red
  n == 16 ? (setcolor(0,0,0,180););  // see thru black
  n == 17 ? (setcolor(255,0,0,120););  // see thru red
  n == 18 ? (setcolor(100,100,100,127););  // octave line
  n == 19 ? (setcolor(100,100,100,255););  // barline
  n == 23 ? (setcolor(130,130,130,255););  // time position not sel
  n == 24 ? (setcolor(80,80,80,255););  // grid squares first beat
  n == 25 ? (setcolor(105,105,105,255););  // grid squares other beats
  n == 31 ? (setcolor(40,40,40,255););  // BG
  n == 32 ? (setcolor(122,122,122,255););  // Divider Top
  n == 33 ? (setcolor(100,100,100,255););  // Divider Center
  n == 34 ? (setcolor(68,68,68,255););  // Divider Bottom
  n == 35 ? (setcolor(255,255,255,255););  // Env Label
  n == 36 ? (setcolor(0,0,0,127););       // velobar, see thru black
  n == 37 ? (setcolor(160,160,160,255);); // button txt labels dark
  n == 38 ? (setcolor(160,160,160,255);); // button txt labels light
  colorscheme == 7 ? (
    colorschemename="Bloody Hell";
    n == 1 ? (setcolor(110,20,20,255););    // button bg colour  
    n == 2 ? (setcolor(150,25,25,102););    // button hilite
    n == 3 ? (setcolor(175,0,0,255););     // button chain ptn
    n == 5 ? (setcolor(180,180,180,255););  // button txt pttn used, labels
    n == 6 ? (setcolor(90,95,95,255););     // button txt pttn empty
    n == 10 ? (setcolor(120,16,16,255););    // dm midi ch num bg
    n == 20 ? (setcolor(200,55,55,127););    // selected first beat
    n == 21 ? (setcolor(255,55,55,127););    // selected other beats
    n == 22 ? (setcolor(255,0,0,200););    // time position selected 
    n == 26 ? (setcolor(190,190,190,255);); // text for vel/shift
    n >26 && n<31 ?(setcolor(150,50,50,255);); // Env 1-4
    ) : (
    colorschemename="Spacebar";
    //n == 1 ? (setcolor(77,77,77,255););   // button bg colour
    n == 1 ? (setcolor(99,99,99,255););     // button bg colour
    n == 2 ? (setcolor(147,147,147,102););  // button hilite
    n == 3 ? (setcolor(160,160,160,255););  // button chain ptn
    n == 5 ? (setcolor(0,0,0,255););        // button txt pttn used, labels
    n == 6 ? (setcolor(140,140,140,255););  // button txt pttn empty
    n == 10 ? (setcolor(37,37,37,255););    // dm midi ch num bg
    n == 20 ? (setcolor(160,160,160,255);); // selected first beat
    n == 21 ? (setcolor(190,190,190,255);); // selected other beats
    n == 22 ? (setcolor(255,255,255,255);); // time position selected
    n == 26 ? (setcolor(150,150,150,255);); // text for vel/shift
    n >26 && n<31 ? (setcolor(180,180,180,255););  // Env 1-4
    );
  );
  colorscheme == 9?  (
  colorschemename="Cyan-ide";
  n == 1 ? (setcolor(209,224,239,255););   // button bg colour
  n == 2 ? (setcolor(146,195,246,102););   // button hilite
  n == 3 ? (setcolor(179,194,209,255););   // button chain ptn
  n == 5 ? (setcolor(120,120,120,255););  // button txt pttn used, labels
  n == 6 ? (setcolor(160,160,160,255););   // button txt pttn empty
  n == 8 ? (setcolor(41,41,41,255););   // drum mode white key background
  n == 9 ? (setcolor(220,220,220,127););  // dm midi ch num txt, see thru black
  n == 10 ? (setcolor(37,37,37,255););  // dm midi ch num bg
  n == 11 ? (setcolor(255,0,0,205););      // dm midi xpose, red
  n == 16 ? (setcolor(0,0,0,180););      // see thru black
  n == 17 ? (setcolor(255,0,0,120););      // see thru red
  n == 18 ? (setcolor(100,100,100,127););  // octave line
  n == 19 ? (setcolor(100,100,100,255););  // barline
  n == 20 ? (setcolor(110,145,175,255););  // selected first beat
  n == 21 ? (setcolor(80,112,134,255););  // selected other beats
  n == 22 ? (setcolor(55,73,88,255););  // time position selected
  n == 23 ? (setcolor(177,209,246,255););  // time position not sel
  n == 24 ? (setcolor(229,244,255,255););  // grid squares first beat
  n == 25 ? (setcolor(209,224,239,255););  // grid squares other beats
  n == 26 ? (setcolor(150,150,150,255););  // text for vel/shift - mid
  n >26 && n < 31 ? (setcolor(80,112,134,255););  // Env 1
  n == 31 ? (setcolor(255,255,255,255););  // BG
  n == 32 ? (setcolor(209,224,239,255););  // Divider Top
  n == 33 ? (setcolor(189,204,219,255););  // Divider Center
  n == 34 ? (setcolor(169,184,199,255););  // Divider Bottom
  n == 35 ? (setcolor(255,255,255,255););  // Env Label
  n == 36 ? (setcolor(255,255,255,200);); // velobar, see thru white
  n == 37 || n == 38 ? (setcolor(120,120,120,255);); // button txt labels dark
  );
);

npatterns=48;          // no of patterns - 48 for startup in either mode
patternmode=0;         // 0 = 96 patterns, 64 steps; 1 = 48 patterns, 128 steps;
xtrapatts=0;           // flag to show when extra 48 patterns have been initialized
max_numnotes=49;       // \
max_seqlength=64;      // max nos
max_steps_per_beat=16; // / 
numnotes=25;           // \
listlength=16;         // start nos
steps_per_beat=4;      // /
ncolorschemes=9;       // no of color schemes
numccs=4;
lbeatpos=-1;
ltickpos=-1;
p=-1; //Current pattern (use -1 here to force call to change_pattern right after init)
prev_p=p;

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////// Memory Allocation ///////
notelist_base       = 0;                            // 024576 
velolist_base       = notelist_base       + 024576; // 301056
new_velolist_base   = velolist_base       + 301056; // 301056
notetie_base        = new_velolist_base   + 301056; // 024576 
sparebuf3           = notetie_base        + 024576; // max_subdivs (8); for subdivision move
sparebuf            = sparebuf3           + 000128; // 100000 for copying notes 
sparebuf2           = sparebuf            + 100000; // 301056 for copying velocities 
sparebuf_tie        = sparebuf2           + 301056; // 050000 for copying note ties 
sparebuf_cc         = sparebuf_tie        + 050000; // 050000 for copying CCs
cclist_base         = sparebuf_cc         + 050000; // 024576  
sdlist              = cclist_base         + 024576; // 2408448 
buf_offset          = sdlist              + 2408448;// 1024       
buf_msg1            = buf_offset          + 001024; // 1024
buf_msg23           = buf_msg1            + 001024; // 1024
start_adj_orig_list = buf_msg23           + 001024; // 1024
nn                  = start_adj_orig_list + 001024; // 96
notenames           = nn                  + 000128; // 011520
has_notes           = notenames           + 011520; // npatterns; flags for patterns with notes.
notetranspose       = has_notes           + 000128; // max_numnotes
notrans             = notetranspose       + 000128; // max_numnotes
listlength_pat      = notrans             + 000128; // npatterns
rowchannel          = listlength_pat      + 000128; // npatterns;    
zerochan            = rowchannel          + 000128; // max_numnotes; 
steps_per_beat_pat  = zerochan            + 000128; // max_numnotes;
cc_type             = steps_per_beat_pat  + 000128; // npatterns;  // numccs
cc_chan             = cc_type             + 000128; // numccs;  // numccs
sd_playstate        = cc_chan             + 000128; // 1024
  // sd_playstate = state of subdivided notes on each playing note row 
  // (2 subsequent values per row: sdnum and sdena)
runl_flags          = sd_playstate        + 001024; 
savedata            = runl_flags          + 1000000;
savedata2           = savedata            + 1000000;
nextslot1           = savedata2           + 1000000;
// last used address  6603392 // 1785215 remaining  
////////////////////////////////////////////////////////// Memory Allocation ///////
////////////////////////////////////////////////////////////////////////////////////

// Mouse state encodings
mstate=0; // OFF
ms_grid=2^8; // note grid area
ms_noteerase=ms_grid+0;
ms_notedraw=ms_grid+1;
ms_veloedit=ms_grid+2;
ms_veloeditrow=ms_grid+3;
ms_notetie=ms_grid+4;
ms_startoffsetedit=ms_grid+5;
ms_startoffseteditrow=ms_grid+6;
ms_subdivnum=ms_grid+7;
ms_pk=2^9; //piano key area
ms_duplseq=ms_pk+0;
ms_rowtranpose=ms_pk+1;
ms_doublespbpreservepos=ms_pk+2; // double steps per beat, preserve note positions
ms_halvespbpreservepos=ms_pk+3; // halve steps per beat, preserve note positions
ms_doublespb=ms_pk+4;
ms_halvespb=ms_pk+5;
ms_tb=2^10;    // toolbar area
ms_tb_r1=2^5;  // toolbar row 1
ms_tb_pianokey_base=ms_tb+ms_tb_r1+0;
ms_tb_pianokey_trig=ms_tb+ms_tb_r1+1;
ms_tb_pianokey_numnotes=ms_tb+ms_tb_r1+2;
ms_tb_r2=2^6;  // toolbar row 2
ms_tb_r3=2^7;  // toolbar row  3
ms_tb_patchange=ms_tb+ms_tb_r2+0;
ms_tb_patcopy=ms_tb+ms_tb_r2+1;
ms_tb_chain=ms_tb+ms_tb_r2+2;
ms_tb_colorbutt=ms_tb+ms_tb_r2+3;
ms_tb_r4=2^13; // toolbar row 4
ms_tb_patshift=ms_tb+ms_tb_r4+1;
ms_tb_button_noundo=ms_tb+ms_tb_r4+2;
ms_tb_button=ms_tb+ms_tb_r4+3;
ms_tb_swing=ms_tb+ms_tb_r4+4;
ms_tb_rate=ms_tb+ms_tb_r4+5;
ms_tb_midimode=ms_tb+ms_tb_r4+7;
ms_tb_r5=2^14; // toolbar row 5
ms_tb_button=ms_tb+ms_tb_r5+0;
ms_tb_panic=ms_tb+ms_tb_r5+1;
ms_tb_notelen=ms_tb+ms_tb_r5+3;
ms_tb_patternlen=ms_tb+ms_tb_r5+4;
ms_tb_numbeats=ms_tb+ms_tb_r5+5;
ms_tb_move=ms_tb+ms_tb_r5+6;
ms_tb_settings=ms_tb+ms_tb_r5+7;
ms_tb_velo=ms_tb+ms_tb_r5+8;
ms_tb_r6=2^15; // toolbar row 6
ms_tb_pattmode=ms_tb+ms_tb_r6+0;
ms_el=2^11; // envelope lane area
ms_el_divider=ms_el+0;
ms_el_envdraw=ms_el+1;
ms_el_enverase=ms_el+2;
ms_el_ctrlchange=ms_el+3;
ms_preview=2^12; // preview note or piano key 
ms_sub_drawfreehand=0; // substate for ms_noteerase and ms_notedraw. 0=off, 1=on
ms_sub_channelchange=0; // substate for ms_rowtranpose and ms_el_ctrlchange

// Toolbar element highlight states
hl_state=0;
hl_pattern=16; // Lowest 4 bits indicate the pattern number to highlight
hl_mode=113;
hl_playbeforestart=114;
hl_startpos=115;
hl_endpos=116;
hl_notelen=117;
hl_swing=118;
hl_patshift=119;
hl_panic=120;
hl_patternlen=121;
hl_numbeats=122;
hl_colorbutt=123;
hl_rate=124;
hl_move=125;
hl_velo=126;
hl_midimode=127;
hl_settings=128;
hl_pattmode=129;
last_hl_state=0;

lastpreviewsel=-1;
gfx_clear=-1;
ext_noinit=1;
noteonstate=0;
want_preview=-1;
last_preview=-1;
want_preview_nt=-1;
last_preview_nt=-1;
want_previewoff=0;
defaultvelo=100;
lastvelo=defaultvelo;

velobarwidth = 3;
ticks = 0;
tpb = 128; // ticks per beat
pianow=3*8+8;
chw = 0; //MIDI channel column width
pkw = pianow+chw; // piano key area width (pianokeys or note_names + MIDI channel column )
tbrh = 18; // toolbar row height
tbh = 5*tbrh+1; // toolbar height
el_divh = 6; // envelope lane divider height
recalc_elh = 1; // recalculate envelope lane height
lbasenote = -1;
lnumnotes = -1;
rateadj = 1;
lrateadj = -1;
lhl = 0;
lmode = -1;
settingsopen=0;

cc_to_adjust=0; // Currently highlighted envelope active for editing
refresh_cc_controls=1; // force refresh at init
want_noteoff_nt = 0;
ldraglen = -1;
start_beatpos = 0;
end_beatpos = -99; // if end_beatpos <= start_beatpos, then play infinitely
recmask = 0xFFFFFFFF;
notelen_p = 100;
defnotelen = tpb;
swing_p = 0;
swing = 0;
rate = 1.0;
notetie=notetie_base;
ltiestate=0;
refresh_tb=1;
busp=24; //toolbar button spacing (pixels)
mouse_on_button=0;
midi_trigger=0;
trig_note_start=96;
pat_trigger_pending=0;
pat_trigger_note=0;
pattrans_trigger_pending=0;
pattrans_trigger_note=0;
chain_beats=listlength/steps_per_beat;
ts_num_l=4;
ts_denom_l=4;
pat_shift=0;
cclist=cclist_base;

nn[0] = strcpy(500, "C");
nn[1] = strcpy(501, "C");
nn[2] = strcpy(502, "D");
nn[3] = strcpy(503, "D");
nn[4] = strcpy(504, "E");
nn[5] = strcpy(505, "F");
nn[6] = strcpy(506, "F");
nn[7] = strcpy(507, "G");
nn[8] = strcpy(508, "G");
nn[9] = strcpy(509, "A");
nn[10] = strcpy(510, "A");
nn[11] = strcpy(511, "B");
#name_str="";
notename_digit=0;
nn_filename=1000; // string slot for notename file name
lnotenames_file=-1;
max_notename_length=4; //Number of characters in note name. This controls the notename field width in GUI. 
max_subdivs=8;
sdlist_p=0; // sdlist location of current pattern
tb_pianokey_end=10*(7*4+5*3+2)+5*4+3*3+1;

//initialize all patterns to same length,steps per beat, midi channels
memset(listlength_pat, listlength, npatterns); 
memset(steps_per_beat_pat, steps_per_beat, npatterns); 
memset(rowchannel, 0, max_numnotes);
listlength_all=listlength*npatterns;

// Set note velocity value to bits (7:0)
function setVelo(note, velo)
(
  velolist[note] &= 0xFFFFFF80; //clear old
  velolist[note] += velo; //set new
);

// Get note velocity value from bits (7:0)
function getVelo(note)
(
  velolist[note] & 0x7F;
);

// Set note start offset to bits (15:8)
function setStart(note, start)
(
  velolist[note] &= 0xFFFF00FF; //clear old
  velolist[note] += (start << 8); //set new
);

// Get note start value from bits (15:8)
function getStart(note)
(
  (velolist[note] & 0x0000FF00) >> 8;
);

// Set number of subdivs to bits (19:16)
function setSubdivNum(note, num)
(
  velolist[note] &= 0xFFF0FFFF; //clear old
  velolist[note] += (num << 16); //set new
);

// Get number of subdivs from bits (19:16)
function getSubdivNum(note)
(
  (velolist[note] & 0x000F0000) >> 16;
);

function setSubdivVelo(row, step, sd_pos, velo)
(
  sdlist[sdlist_p+row*max_seqlength*max_subdivs+step*max_subdivs+sd_pos] &= 0xFFFFFF80; //clear old
  sdlist[sdlist_p+row*max_seqlength*max_subdivs+step*max_subdivs+sd_pos] += velo;
);

function getSubdivVelo(row, step, sd_pos)
(
  sdlist[sdlist_p+row*max_seqlength*max_subdivs+step*max_subdivs+sd_pos] & 0x7F;
);

// Set subdiv notelist indicating which subdiv steps are enabled
function setSubdivNotelist(row, step, notes)
(
  sdlist[sdlist_p+row*max_seqlength*max_subdivs+step*max_subdivs] &= 0xFF00FFFF; //clear old
  sdlist[sdlist_p+row*max_seqlength*max_subdivs+step*max_subdivs] += (notes << 16); //set new
  
);

// Get subdiv notelist indicating which subdiv steps are enabled
function getSubdivNotelist(row, step)
(
  (sdlist[sdlist_p+row*max_seqlength*max_subdivs+step*max_subdivs] & 0x00FF0000) >> 16;
);

// Get CC value from source's bit field indicated by ccpos
function getCc(source, ccpos) local(val)
(
  val = 0;
  ccpos == 0 ? val = source & 0xFF;
  ccpos == 1 ? val = (source & 0x0000FF00) >> 8;
  ccpos == 2 ? val = (source & 0x00FF0000) >> 16;
  ccpos == 3 ? (
    source & 0x80000000 ? ( // bit 31 = 1
      val = ((source & 0x7F000000) >> 24) + 0x80;
    ) : (
      val = (source & 0x7F000000) >> 24;
    );
  );
  val;
);

// Set CC value val to target's bit field indicated by ccpos
function setCc(target, ccpos, val)
(
  ccpos == 0 ? (target &= 0xFFFFFF00; target += val;);
  ccpos == 1 ? (target &= 0xFFFF00FF; target += val * 2^8;);
  ccpos == 2 ? (target &= 0xFF00FFFF; target += val * 2^16;);
  ccpos == 3 ? (target &= 0x00FFFFFF; target += val * 2^24;);
  target;
);

// Is the value within valid MIDI value range (7bit)?
function is_midi_val(value) 
(
  valid = 0;
  value >= 0 && value < 128 ? valid = 1;
  valid;
);

// Truncate to valid MIDI value range (7bit).
function trunc_midi_val(value) 
(
  value < 0 ? (
    value = 0;
  ) : (
    value > 127 ? value = 127;
  );
  value;
);

function scale_velobarwidth(seqlen) local(width, gfx_w_l)
(
  width = 2;
  gfx_w == 0 ? gfx_w_l=400+pkw : gfx_w_l=gfx_w;
  seqlen/(gfx_w_l/(400+pkw)) > 65 ? width = 1;
  seqlen/(gfx_w_l/(400+pkw)) < 42 ? width = 3;
  width; // return scaled width
);

// Update flags indicating patterns that are not empty
function update_has_notes() local(p, step, note_found, notelist, cclist)
(
  //has_notes=0;
  notelist=notelist_base;
  cclist=cclist_base;
  p=0;
  loop(npatterns,
    
    note_found=0;
    step=0;
    while(step < listlength_pat[p] && !note_found ? (
        notelist[step] != 0 ? note_found=1;
        cclist[step] != 0 ? note_found=1;
        step+=1;
      );
    );
    note_found ? (has_notes[p]=1;):(has_notes[p]=0;);
    notelist+=listlength_pat[p];
    cclist+=listlength_pat[p];
    p+=1;
  );
);

// Change pattern
function change_pattern(new_p) local(i)
(
  new_p<0 ? new_p=0 : new_p>=npatterns ? new_p=npatterns-1;
  p=new_p;
  notelist=0;
  velolist=velolist_base;
  i=0;
  loop(p,
    notelist+=listlength_pat[i];
    velolist+=max_numnotes*listlength_pat[i];
    i+=1;
  );
  notetie=notetie_base+notelist;
  cclist=cclist_base+notelist;
  notelist=notelist_base+notelist;
  
  listlength=listlength_pat[p];
  velobarwidth = scale_velobarwidth(listlength);
  steps_per_beat = steps_per_beat_pat[p];
  rateadj = steps_per_beat * rate;
  update_has_notes();
  refresh_tb=1;
  ltiestate = 0; // clear all note ties
  sdlist_p=p*max_numnotes*max_seqlength*max_subdivs;
);

// Update chain beats
function update_chain_beats() local(i_pat)
(
  chain_beats=listlength_pat[0]/steps_per_beat_pat[0];
  i_pat=1;
  loop(chain,
    chain_beats+=listlength_pat[i_pat]/steps_per_beat_pat[i_pat];
    i_pat+=1;
  );
);

// Change sequence length
function change_seqlen(newsz) local(ip, y, pstart, pstartv, pstart_n, pstartv_n, new_listlength_all)
(
  newsz >= 1 && listlength_pat[p] != newsz ? (
    
    new_listlength_all=listlength_all-listlength_pat[p]+newsz;
    
    newsz < listlength_pat[p] ? ( // decrease size
    
      pstart=0; // old pattern start offset for notes
      pstartv=0; // old pattern start offset for velocities
      pstart_n=0; // new pattern start offset for notes
      pstartv_n=0; // new pattern start offset for velocities
      ip=0; // loop index iterating over patterns
      loop(npatterns,
        memcpy(sparebuf + ip*1024, pstart, listlength_pat[ip]); // save a copy of the full quality notelist
        memcpy(sparebuf_tie + ip*1024, notetie_base+pstart, listlength_pat[ip]); // save a copy of the full quality notetielist 
        memcpy(sparebuf_cc + ip*1024, cclist_base+pstart, listlength_pat[ip]); // save a copy of the full quality notetielist 
        y=0;
        loop(max_numnotes,
          memcpy(sparebuf2 + ip*max_numnotes*max_seqlength+y*max_seqlength, velolist_base+pstartv+y*listlength_pat[ip], listlength_pat[ip]); // save a copy of the full quality
          y+=1;
        );

        ip < p ? (
          memcpy(new_velolist_base+pstartv_n, velolist_base+pstartv, max_numnotes*listlength_pat[ip]);
          pstart_n+=listlength_pat[ip];
          pstartv_n+=max_numnotes*listlength_pat[ip];
        ) : ip == p ? (
          memcpy(pstart_n, pstart, newsz);
          memcpy(notetie_base+pstart_n, notetie_base+pstart, newsz);
          memcpy(cclist_base+pstart_n, cclist_base+pstart, newsz);
          y=0;
          loop(max_numnotes,
            memcpy(new_velolist_base+pstartv_n+y*newsz, velolist_base+pstartv+y*listlength_pat[ip], newsz);
            y+=1;
          );
          pstart_n+=newsz;
          pstartv_n+=max_numnotes*newsz;
        ) : (
          memcpy(pstart_n, pstart, listlength_pat[ip]);
          memcpy(notetie_base+pstart_n, notetie_base+pstart, listlength_pat[ip]);
          memcpy(cclist_base+pstart_n, cclist_base+pstart, listlength_pat[ip]);
          memcpy(new_velolist_base+pstartv_n, velolist_base+pstartv, max_numnotes*listlength_pat[ip]);
          pstart_n+=listlength_pat[ip]; 
          pstartv_n+=max_numnotes*listlength_pat[ip];          
        );
        
        pstart+=listlength_pat[ip];
        pstartv+=max_numnotes*listlength_pat[ip];
        ip+=1;
      ); 
      
    ) : ( // increase size
    
      pstart=listlength_all; // old pattern start offset for notes
      pstartv=max_numnotes*listlength_all; // old pattern start offset for velocities
      pstart_n=new_listlength_all; // new pattern start offset for notes
      pstartv_n=max_numnotes*new_listlength_all; // new pattern start offset for velocities
      
      ip=npatterns-1; // loop index iterating over patterns in reverse order
      loop(npatterns,      
        pstart-=listlength_pat[ip];
        pstartv-=max_numnotes*listlength_pat[ip];
        
        ip < p ? (
          pstart_n-=listlength_pat[ip];
          pstartv_n-=max_numnotes*listlength_pat[ip];
          memcpy(new_velolist_base+pstartv_n, velolist_base+pstartv, max_numnotes*listlength_pat[ip]);
        ) : ip == p ? (
          pstart_n-=newsz;
          pstartv_n-=max_numnotes*newsz;
          y=0;
          loop(max_numnotes,
            memcpy(new_velolist_base+pstartv_n+y*newsz, velolist_base+pstartv+y*listlength_pat[ip], listlength_pat[ip]);
            memcpy(new_velolist_base+pstartv_n+y*newsz+listlength_pat[ip], sparebuf2+ip*max_numnotes*max_seqlength+y*max_seqlength+listlength_pat[ip], newsz-listlength_pat[ip]);
            y+=1;
          );
          memcpy(cclist_base+pstart_n, cclist_base+pstart, listlength_pat[ip]);
          memcpy(cclist_base+pstart_n+listlength_pat[ip], sparebuf_cc+ip*1024+listlength_pat[ip], newsz-listlength_pat[ip]);
          memcpy(notetie_base+pstart_n, notetie_base+pstart, listlength_pat[ip]);
          memcpy(notetie_base+pstart_n+listlength_pat[ip], sparebuf_tie+ip*1024+listlength_pat[ip], newsz-listlength_pat[ip]);
          memcpy(pstart_n, pstart, listlength_pat[ip]);
          memcpy(pstart_n+listlength_pat[ip], sparebuf+ip*1024+listlength_pat[ip], newsz-listlength_pat[ip]);
        ) : (
          pstart_n-=listlength_pat[ip]; 
          pstartv_n-=max_numnotes*listlength_pat[ip]; 
          memcpy(new_velolist_base+pstartv_n, velolist_base+pstartv, max_numnotes*listlength_pat[ip]);
          memcpy(cclist_base+pstart_n, cclist_base+pstart, listlength_pat[ip]);
          memcpy(notetie_base+pstart_n, notetie_base+pstart, listlength_pat[ip]);
          memcpy(pstart_n, pstart, listlength_pat[ip]);
        );
        ip-=1;
      );
    );   
    
    listlength_pat[p] = newsz;
    temp = new_velolist_base;
    new_velolist_base = velolist_base;
    velolist_base = temp;
    
    listlength = newsz; // Update current sequence length
    listlength_all = new_listlength_all;
    //change_pattern(p);
  
    velolist=velolist_base;
    ip=0;
    loop(p,
      velolist+=max_numnotes*listlength_pat[ip];
      ip+=1;
    );
    
    velobarwidth = scale_velobarwidth(listlength);
    
    p <= chain ? update_chain_beats();

  );
);

function moveUpDown(dir) // 1 Up // 0 Down //
(
  step=0; endrowused=0;
  loop(listlength,
    dir?(notelist[step]>281474976710655 ? endrowused+=1;):(endrowused+=notelist[step]&1;);
    step+=1; 
  );
  endrowused==0? (
    step=0; 
    loop(listlength,
    notelist[step]=dir?(notelist[step]*2;):(notelist[step]/2;);
    notetie[step]= dir?(notetie[step]*2;):(notetie[step]/2;);
    dir?(row=max_numnotes;):(row=0;);
    loop(max_numnotes,
      sd_pos=0;
    loop(max_subdivs,
        sdlist[sdlist_p+row*max_seqlength*max_subdivs+step*max_subdivs+sd_pos] = sdlist[sdlist_p+
      ((dir?(row-1):(row+1);)*max_seqlength*max_subdivs)+step*max_subdivs+sd_pos];
        sd_pos+=1;
      );  
    velolist[step+(row*listlength)] = velolist[step+((dir?(row-1):(row+1);)*listlength)];
      dir?(row-=1;):(row+=1;);
    );
    step+=1;
    );
  );       
  dir?(memset(velolist, 0, listlength););    
);

function moveRightLeft(dir,ccs) // 1 Right // 0 Left // 
(
  y=dir?(-1):(1);
  z=dir?(listlength-1):(0);
  x=dir?(0):(listlength-1);
  sparebuf[0] = notelist[z];
  sparebuf_tie[0] = notetie[z]; 
  ccs ? sparebuf_cc[0] = cclist[z];
  step=z;
  loop(listlength-dir,   
    notelist[step] = notelist[step+y]; 
    notetie[step] = notetie[step+y];
    ccs ? cclist[step] = cclist[step+y];
    dir?(step-=1;):(step+=1;);
  );
  notelist[x] = sparebuf[0];
  notetie[x] = sparebuf_tie[0];
  ccs ? cclist[x] = sparebuf_cc[0];
  row=0;  
  loop(max_numnotes,
    step=z;
    sparebuf2[0]=velolist[dir?((row+1)*listlength-1):(row*listlength);];
    sd_pos=0;
    loop(max_subdivs,
      sparebuf3[sd_pos]=sdlist[sdlist_p+row*max_seqlength*max_subdivs+step*max_subdivs+sd_pos];
      sd_pos+=1;
    );  
    loop(listlength-1,    
      sd_pos=0;
      loop(max_subdivs,
        sdlist[sdlist_p+row*max_seqlength*max_subdivs+step*max_subdivs+sd_pos] = sdlist[sdlist_p+row*max_seqlength*max_subdivs+(step+y)*max_subdivs+sd_pos];
        sd_pos+=1;
      ); 
      velolist[step+(row*listlength)] = velolist[step+(row*listlength)+y];
      step+=y; 
    );
    velolist[dir?(row*listlength):(((row+1)*listlength)-1);]=sparebuf2[0];
    sd_pos=0;
    loop(max_subdivs,
      sdlist[sdlist_p+row*max_seqlength*max_subdivs+x*max_subdivs+sd_pos] = sparebuf3[sd_pos];
      sd_pos+=1;
    );
  row+=1;
  );
);

function initialize48(pm)
(
  //change number of patterns: initialize 48 patts ONLY, set max_seqlength
  patternmode=pm; max_seqlength = pm==0?(64):(128);
  listlength = 16; steps_per_beat=4; npatterns = 48;
  p=0; pat_shift=0; xtrapatts=0; chain=0;
  sliderchange(slider1=p);
  sliderchange(slider3=listlength);
  sliderchange(slider9=steps_per_beat);
  sliderchange(slider44=pat_shift);
  sliderchange(slider46=xtrapatts);
  sliderchange(slider12=chain);
  sliderchange(slider47=npatterns);
  memset(sparebuf,      0, npatterns*1024);
  memset(sparebuf_tie,  0, npatterns*1024);
  memset(sparebuf_cc,   0, npatterns*1024);
  memset(notelist_base, 0, npatterns*max_seqlength);
  memset(notetie_base,  0, npatterns*max_seqlength);
  memset(cclist_base,   0, npatterns*max_seqlength); 
  memset(sparebuf2,     0, npatterns*max_seqlength*max_numnotes);
  memset(velolist_base, 0, npatterns*max_seqlength*max_numnotes); 
  memset(sdlist,        0, npatterns*max_seqlength*max_numnotes*max_subdivs);
  memset(rowchannel,    0, max_numnotes);
  memset(notetranspose, 0, max_numnotes);
  memset(listlength_pat, listlength, npatterns); 
  memset(steps_per_beat_pat, steps_per_beat, npatterns); 
  listlength_all=listlength*npatterns;
  change_pattern(p);
  update_has_notes();
  refresh_tb=1;
);
@slider

new_p=slider1|0;
new_p!=p ? change_pattern(new_p);
basenote=slider2|0; 
numnotes=slider4|0;
rate=slider5;
notelen_p = slider6|0;
defnotelen = (tpb*(notelen_p/100))|0;
mode = slider7|0; //0= piano roll, 1 = drum map
mode == 0 ? (
  chw=0;
  pianow=3*8+8;
) : (
  chw = 2*8+8;
  pianow=max_notename_length*8+8;
);
pkw=chw+pianow;
swing_p = slider8|0;
swing = ((tpb/2)*(swing_p/100))|0;
midi_trigger=slider10|0;
prev_p==p ? ( // not changing pattern
  newsz=slider3|0;
  change_seqlen(newsz);
  steps_per_beat=slider9|0;
  steps_per_beat_pat[p]=steps_per_beat;
  rateadj = steps_per_beat * rate;
  p <= chain ? update_chain_beats();
);
trig_note_start=slider11|0;
chain=slider12|0;
elh_percent=slider13;
notenames_file=slider15|0;
cc_to_adjust=slider14|0;
cc_type[0]=slider20|0;
cc_type[1]=slider21|0;
cc_type[2]=slider22|0;
cc_type[3]=slider23|0;
cc_chan[0]=slider30|0;
cc_chan[1]=slider31|0;
cc_chan[2]=slider32|0;
cc_chan[3]=slider33|0;
start_beatpos=slider40;
play_before_start=slider41|0;
end_beatpos=slider42;
colorscheme=slider43;
pat_shift=slider44;
pm=slider45;
xtrapatts=slider46;
npatterns=slider47;
pm!=patternmode ? (
 initialize48(pm); 
);

@serialize
// Write:
// collect properties of active notes -> runlength encode -> encode to saveable -> serialize
//
// Read:
// unserialize -> decode from saveable -> runlength decode -> expand note properties
//

function enc_to_saveable(target, source, length) local(i, i_t, s)
(
  i=0;
  i_t=0;
  loop(length,
    s = source[i];
    s < 0 ? ( // negative source value
      target[i_t] = 0x200000; // add flag to bit 21 indicating negative value
      s = abs(s); // store absolute value
    ) : (
      target[i_t] = 0;
    );
    s > 0xFFFFF ? (//using more than 20 bits -> split
      target[i_t] += (s & 0xFFFFF) + 0x100000; // copy bits (19:0) and add flag to bit 20
      i_t += 1;
      s & 0x80000000 ? ( // bit 31 = 1
        target[i_t] = ((s & 0x7FFFFFFF) >> 20) + 0x800; // copy bits (31:20)
      ) : (
        target[i_t] = ((s & 0x7FFFFFFF) >> 20); // copy bits (31:20)
      );
    ) : (
      runl_flags[i] ? (
        target[i_t] = s + 2^23; //bit 23 flags a run length. It is safe to do because length of run is always < 2^23 and > 0.
      ) : (
        target[i_t] += s;
      );
    );
    i += 1;
    i_t += 1;
  );
  i_t; // return length of target
);

function dec_from_saveable(target, source, length) local(i, i_t, s)
(
  memset(runl_flags, 0, length); //clear run length flags
  i=0;
  i_t=0;
  loop(length,
    s = source[i];
    s_neg = s & 0x200000; // extract negative value flag
    s_neg ? s -= 0x200000; //clear negative value flag
    s & 0x100000 ? ( // split data flag
      s &= 0xFFFFF; // extract bits (19:0)
      i+=1;
      s += source[i] * 2^20; // extract bits (31:20)
      target[i_t] = s;
    ) : (
      s & 0x800000 ? ( // run length flag
        runl_flags[i_t] = 1; // copy run length flag 
        target[i_t] = s & 0xFFFFF; // remove flag and copy source data to target
      ):(
        target[i_t] = s; // copy to target
      );
    );
    s_neg ? target[i_t]=-target[i_t]; // negate target data if source data was flagged negative
    i += 1;
    i_t += 1;
  );
  i_t; // return length of target
);

// Run length encoder
function runl_enc(target, source, length) local(i, i_t, s, run, prev_s, rl)
(
  memset(runl_flags, 0, length); //clear run length flags
  i_t=0;
  rl=1;
  run=0;
  prev_s=source[0];
  i=1;
  loop(length-1,
    s = source[i];
    s==prev_s ? (
      run=1;
      rl+=1; 
    ) : (
      run ? ( //end of run
        run=0;
        target[i_t]=rl; //store run length
        rl=1;
        runl_flags[i_t]=1; // flag run length position
        i_t += 1;
        target[i_t]=prev_s; // store data to be repeated
      ) : ( //there was no run active
        target[i_t]=prev_s; // store previous source data
      );
      i_t += 1;
    );
    prev_s=s;
    i+=1;
  );
  // End of source data
  run ? ( // Data ends in a run
    target[i_t]=rl; //store run length
    runl_flags[i_t]=1; // flag run length position
    i_t += 1;
    target[i_t]=prev_s; // store data to be repeated
  ) : ( //no run in the end
    target[i_t]=prev_s; // store previous source data
  );
  i_t + 1; // return length of target
);

// Run length decoder
function runl_dec(target, source, length) local(i, i_t)
(
  i=0;
  i_t=0;
  while(
    runl_flags[i] ? ( // Current source data is a run length
      memset(target+i_t, source[i+1], source[i]); // Fill target with next source data. Fill length = run length. 
      i_t+=source[i];
      i+=1;
    ) : (
      target[i_t]=source[i];
      i_t+=1;
    );
    i+=1;
    i < length;
  );
  i_t; // return length of target
);

serialread= (file_avail(0) >= 0);

serilen=0;

!serialread ?(
  memcpy(savedata+serilen, cclist_base, listlength_all);
  serilen += (listlength_all);
  memcpy(savedata+serilen, notetranspose, max_numnotes);
  serilen += max_numnotes;
  memcpy(savedata+serilen, rowchannel, max_numnotes);
  serilen += max_numnotes;
  memcpy(savedata+serilen, listlength_pat, npatterns);
  serilen += npatterns;
  memcpy(savedata+serilen, steps_per_beat_pat, npatterns);
  serilen += npatterns; pat = 0;
  pstart = notelist_base;
  pstartv = velolist_base;
  pstartsd = sdlist;
  tstart = notetie_base;
  loop ( npatterns,
    step = 0;
    loop ( listlength_pat[pat],
      notelist_buffer1_save=pstart[step]&0xFFFFFFFF;
      savedata[serilen] = notelist_buffer1_save;serilen += 1;
      notelist_buffer2_save=pstart[step]/(2^32);
      notelist_buffer2_save=notelist_buffer2_save&0xFFFFFFFF;
      savedata[serilen] = notelist_buffer2_save; serilen += 1;
      notetie_buffer1_save=tstart[step]&0xFFFFFFFF;
      savedata[serilen] = notetie_buffer1_save; serilen += 1;
      notetie_buffer2_save=tstart[step]/(2^32);
      notetie_buffer2_save=notetie_buffer2_save&0xFFFFFFFF;
      savedata[serilen] = notetie_buffer2_save; serilen += 1;
      stepnotes = pstart[step];
        stepnotes!=0 ? ( // has notes in current step
        row = 0;
        while (
          (stepnotes&1) ? ( // note on current row
            velo_save = pstartv[row*listlength_pat[pat]+step];
            savedata[serilen] = velo_save;
            serilen += 1;
            (velo_save & 0x000F0000) ? ( // has subdivs
              subdivnotelist_save=((pstartsd[row*max_seqlength*max_subdivs+step*max_subdivs] & 0x00FF0000) >> 16);
              savedata[serilen] = subdivnotelist_save;
              serilen += 1;
              sdpos_save=0;
              while (
                (subdivnotelist_save&1) ? ( // save only active subdiv notes
                  savedata[serilen] = pstartsd[row*max_seqlength*max_subdivs+step*max_subdivs+sdpos_save];
                  serilen += 1;
                );
                subdivnotelist_save *= 0.5;
                sdpos_save += 1;
                subdivnotelist_save>=1;
              );
            );
          );
          stepnotes *= 0.5;
          row +=1;
          stepnotes>=1;
        );
      );
      step += 1;
    );
    tstart += listlength_pat[pat];
    pstart += listlength_pat[pat];
    pstartv += listlength_pat[pat]*max_numnotes;
    pstartsd += max_numnotes*max_seqlength*max_subdivs;
    pat += 1;
  );
  serilen = runl_enc(savedata2, savedata, serilen);
  serilen = enc_to_saveable(savedata, savedata2, serilen);
);

file_var(0,listlength);
file_var(0,serilen);
file_mem(0,savedata,serilen);
file_var(0,serz_data_ver);
file_var(0,listlength_all);
file_var(0,lastvelo);

serialread ?(

  serilen = dec_from_saveable(savedata2, savedata, serilen);
  serilen = runl_dec(savedata, savedata2, serilen);
 
  // clear spare buffers, noteties & subdivs first
  memset(sparebuf, 0, npatterns*1024);
  memset(sparebuf2, 0, npatterns*max_numnotes*max_seqlength);
  memset(sparebuf_tie, 0, npatterns*1024);
  memset(sparebuf_cc, 0, npatterns*1024);
  memset(sdlist, 0, npatterns*max_numnotes*max_seqlength*max_subdivs);
  
  i_srz = 0;
  memcpy(cclist_base, savedata+i_srz, listlength_all); // read CCs from saved data
  i_srz += listlength_all;
  memcpy(notetranspose, savedata+i_srz, max_numnotes);
  i_srz += max_numnotes;
  memcpy(rowchannel, savedata+i_srz, max_numnotes);
  i_srz += max_numnotes;
  memcpy(listlength_pat, savedata+i_srz, npatterns); // read pattern lengths from saved data
  i_srz += npatterns;
  memcpy(steps_per_beat_pat, savedata+i_srz, npatterns); // read steps_per_beat from saved data
  i_srz += npatterns;

  pat = 0;
  tstart = notetie_base;
  pstart = notelist_base;
  pstartv = velolist_base;
  pstartsd = sdlist;
  loop (npatterns,
    step = 0;
    memset(pstartv, 0, max_numnotes*listlength_pat[pat]); //initialize all velocities of pat to 0
    loop ( listlength_pat[pat],
    notelist_buffer1_save = savedata[i_srz];  i_srz += 1;
    notelist_buffer2_save = savedata[i_srz];  i_srz += 1;
    stepnotes = pstart[step]=(notelist_buffer1_save+(notelist_buffer2_save*(2^32)));
      notetie_buffer1_save = savedata[i_srz];  i_srz += 1;
    notetie_buffer2_save = savedata[i_srz];  i_srz += 1;
    tstart[step]=(notetie_buffer1_save+(notetie_buffer2_save*(2^32)));
      stepnotes!=0 ? (
        row = 0;
        while (
          (stepnotes&1) ? (
            velo_save = savedata[i_srz];
            i_srz += 1;
            pstartv[row*listlength_pat[pat]+step] = velo_save;
             // read subdivs from savedata
            (velo_save & 0x000F0000) ? ( // has subdivs
              subdivnotelist_save = savedata[i_srz];
              i_srz += 1;
              subdivnotelist_save_orig = subdivnotelist_save;
              sdpos_save=0;
              while (
                (subdivnotelist_save&1) ? ( // restore active subdiv notes
                  pstartsd[row*max_seqlength*max_subdivs+step*max_subdivs+sdpos_save] = savedata[i_srz];
                  i_srz += 1;
                );
                subdivnotelist_save *= 0.5;
                sdpos_save += 1;
                subdivnotelist_save>=1;
              );
              // set subdivnotelist
              pstartsd[row*max_seqlength*max_subdivs+step*max_subdivs] &= 0xFF00FFFF; //clear old
              pstartsd[row*max_seqlength*max_subdivs+step*max_subdivs] += (subdivnotelist_save_orig << 16); //set new
            );
          );
          stepnotes *= 0.5;
          row += 1;
          stepnotes>=1;
        );
      );
      step += 1;
    );
    tstart += listlength_pat[pat];
    pstart += listlength_pat[pat];
    pstartv += listlength_pat[pat]*max_numnotes;
    pstartsd += max_numnotes*max_seqlength*max_subdivs;
    pat += 1;
  );
  recalc_elh = 1; // recalculate envelope lane height
  lbasenote = -1; // Force graphics redraw
  lnotenames_file = -1; // Force notenames reload
  change_pattern(slider1|0);
  update_chain_beats();
);

@block

ts_num_l=ts_num;
ts_denom_l=ts_denom;
  
function all_playing_notes_off()
(
  npos=0;
  noneed=noteonstate;
  noteonstate=0;
  while(
    (noneed&1) ? (
      midisend(cursplpos,0x80+chan[npos],npos+basenote+trans[npos]); 
    );
    npos+=1;
    noneed*=0.5;
    noneed >= 1;
  );
);

prev_p!=p ? (
  prev_p=p;
  sliderchange(slider3=listlength);
  sliderchange(slider9=steps_per_beat);
);
 
mode == 1 ? (
  trans=notetranspose;
  chan=rowchannel;
):( 
  trans=notrans;
  chan=zerochan;
);

want_previewoff && last_preview >=0 ? (
  midisend(0,0x80+chan[last_preview],last_preview+basenote+trans[last_preview]);
  last_preview=-1;
);
want_previewoff=0;

want_previewoff_nt && last_preview_nt >=0 ? (
  midisend(0,0x80+preview_nt_chan,last_preview_nt+basenote+trans[last_preview_nt]);
  last_preview_nt=-1;
);
want_previewoff_nt=0;

want_noteoff_nt ? (
  is_midi_val(prev_nt) ? midisend(0,0x80+preview_nt_chan,prev_nt);
);
want_noteoff_nt=0;

want_preview >=0 ?
(
  is_midi_val(note_to_send = want_preview+basenote+trans[want_preview]) ? (
    midisend(0,0x90+chan[want_preview],note_to_send+previewvelo*256);
    last_preview=want_preview;
  );
  want_preview=-1;
);

want_preview_nt >=0 ?
(
  is_midi_val(note_to_send = want_preview_nt+basenote+trans[want_preview_nt]) ? (
    preview_nt_chan=chan[want_preview_nt];
    midisend(0,0x90+preview_nt_chan,note_to_send+lastvelo*256);
    last_preview_nt=want_preview_nt;
  );
  want_preview_nt=-1;
);

chain > 0 && beatpos == listlength-1 && pat_trigger_pending == 0? (
  p < chain ? (
    pat_trigger_num=p+1;
    pat_trigger_pending=1;
  ) : p==chain ? (
    pat_trigger_num=0;
    pat_trigger_pending=1;
  );
);

i_buf=0;
pat_triggered=0;
cur_note_trigger=0;
while (
  midirecv(offset,msg1,msg23) ? (
    midi_trigger > 0  ? ( // trigger enabled 
      s = msg1&0xF0;
      n=msg23&0x7F;        
      vel=(msg23/256)|0;
      s==0x90 && vel > 0 ? ( // Note-On event
        n >= trig_note_start && n < trig_note_start+npatterns ? ( // Note within pattern change trigger range
          !pat_triggered ? ( // Not yet triggered pattern
            pat_trigger_num=n-trig_note_start;
            pat_trigger_pending=1;
            pat_triggered=1;
            midi_trigger > 2 ? ( // trigger resync enabled
              beat_position >= -100 && beat_position <= 9999 ? (
                start_beatpos = beat_position; // set start beat position to current beat_position
                midi_trigger > 4 ? ( // trigger resync qunatizing enabled
                  start_beatpos_float = start_beatpos;
                  start_beatpos = start_beatpos_float|0;
                  start_beatpos_float - start_beatpos > 0.5 ? start_beatpos+=1;
                );
              );
              sliderchange(slider40=start_beatpos);
              refresh_tb=1;
            );
          );
          cur_note_trigger=1; // Always flag cur_note_trigger, even when already triggered, to keep trigger notes silent.
        ) : midi_trigger==2 || midi_trigger==4 || midi_trigger==6 ? ( // Transpose enabled
          n != basenote ? (
            pattrans_trigger_pending=1;
            pattrans_trigger_note=n;
          );
          cur_note_trigger=1; // Always flag cur_note_trigger to keep trigger notes silent.
        );
      ) : (s==0x90 && vel == 0) || s==0x80 ? ( // Note-off event
        n >= trig_note_start && n < trig_note_start+npatterns ? ( // Note within pattern change trigger range
          cur_note_trigger=1; // Always flag cur_note_trigger, even when already triggered, to keep trigger notes silent.
        ) : midi_trigger==2 || midi_trigger==4 || midi_trigger==6 ? ( // Transpose enabled
          cur_note_trigger=1; // Always flag cur_note_trigger to keep trigger notes silent.
        );
      );
    );
    !cur_note_trigger ? ( // This note was not triggering pattern change
      buf_offset[i_buf]=offset;
      buf_msg1[i_buf]=msg1;
      buf_msg23[i_buf]=msg23;
      i_buf+=1;
    );
    cur_note_trigger=0;
    loop_to_start=1; // just to keep while loop repeating
  );
);

beat_position >= start_beatpos ? (
  curbeatpos = beat_position - start_beatpos;
) : (
  chain > 0 && p <= chain ? (
    pat_beats=chain_beats / rate;
  ) : (
    pat_beats=listlength/steps_per_beat / rate;
  );
  curbeatpos = beat_position - (start_beatpos - pat_beats * ceil((start_beatpos - beat_position)/pat_beats));
);

cursplpos=0;
dbeatpos = (tempo * 4.0 / (60.0 * ts_denom * srate));

beat_position_int=beat_position;

loop((play_state&1) ? samplesblock : 1,
  
  play_state_i=play_state;
  (!play_before_start && beat_position_int < start_beatpos) || (end_beatpos > start_beatpos && beat_position_int >= end_beatpos) ? (
    play_state_i&=0xFFFE; // set bit 0 to 0. Prevents playback.
  );
  
  chain > 0 && p <= chain ? (
    chains_passed_full=floor(curbeatpos/(chain_beats / rate));
    beats_remaining=curbeatpos-((curbeatpos/(chain_beats / rate))|0)*(chain_beats / rate);
    chain_preceding_beats=0;
    chain_pat_beats = listlength_pat[0]/steps_per_beat_pat[0] / rate;
    i_cp=0;
    beats_remaining >= chain_pat_beats ? (
      while(
        chain_preceding_beats+=chain_pat_beats;
        i_cp+=1;
        chain_pat_beats = listlength_pat[i_cp]/steps_per_beat_pat[i_cp] / rate;
        chain_preceding_beats+chain_pat_beats < beats_remaining;
      );
    );
    !pat_trigger_pending && i_cp != p && (play_state_i&1) ? (
      pat_trigger_num=i_cp;
      pat_trigger_pending=1;
    );
    curbeatpos_c=curbeatpos-chains_passed_full*(chain_beats / rate)-chain_preceding_beats;
    curbeatpos_c < 0 ? ( // Position outside current pattern. Produces unpredictable behaviour. May happen if a pattern in chain contains incomplete beat(s).
      curbeatpos_c=0;
      debug_hits+=1;
    );
    beatpos=(play_state_i&1) ? (curbeatpos_c * rateadj)%listlength : -100;
    tickpos=(play_state_i&1) ? (curbeatpos_c * rateadj * tpb)%(listlength * tpb) : -100;
  ) : (
    beatpos=(play_state_i&1) ? (curbeatpos * rateadj)%listlength : -100;
    tickpos=(play_state_i&1) ? (curbeatpos * rateadj * tpb)%(listlength * tpb) : -100;
  );
  
  tickpos != ltickpos && (beatpos == lbeatpos) && (play_state_i&1) ? (
    npos=0;
    noneed=noteonstate;
    a = swingstate;
    ltiestate_t=ltiestate;
    swing > 0 ? (defnotelen+swing < tpb ? notelen_swinged=defnotelen+swing : notelen_swinged=tpb;);
    while(
      sdnum=getSubdivNum(npos*listlength+beatpos);
      sdnum == 0 ? ( // not subdiv
        start_offset = getStart(npos*listlength+beatpos);
        swing == 0 ? (defnotelen+start_offset < tpb ? notelen_offseted=defnotelen+start_offset : notelen_offseted=tpb;);
        (noneed&1) && (
        (swing > 0 && ((swing_beat && notelen_swinged == ticks) || (!swing_beat && defnotelen == ticks)))
        ||(swing == 0 && notelen_offseted == ticks)) && !(ltiestate_t&1) ? (
          midisend(cursplpos,0x80+chan[npos],npos+basenote+trans[npos]); 
          noteonstate &= (0xFFFFFFFF ~ (1 << npos));
        );
        ((swing_beat && swing > 0 && swing == ticks) || (swing == 0 && getStart(npos*listlength+beatpos) == ticks)) && (a&1) ? (
          is_midi_val(note_to_send = npos+basenote+trans[npos]) ? (
            !(ltiestate_t&1) ? ( // send new note only when there is no tie from previous note
              midisend(cursplpos,0x90+chan[npos],note_to_send + getVelo(npos*listlength+beatpos)*256);
            ) : (
              ltiestate-=2^npos; // clear old tiestate
            );
            noteonstate+=2^npos;
            (swingtiestate & 2^npos) && (beatpos < listlength-1) ? ltiestate+=2^npos; // Set tie state if tie enabled and not last note of sequence
          );
        );
      ) : ( // subdiv note
        ticks > 0 && sd_playstate[2*npos] ? (
          swing_beat ? (
            sd_ticks=((tpb-swing) / (sd_playstate[2*npos]+1))|0;
          ) : (
            sd_ticks=(tpb / (sd_playstate[2*npos]+1))|0;
          );
          (!swing_beat && (ticks % sd_ticks) == 0) || (swing_beat && ticks >= swing && ((ticks-swing) % sd_ticks) == 0 ) ? (
            !swing_beat ? (
              sdpos=(ticks/sd_ticks)|0;
            ) : (
              sdpos=((ticks-swing)/sd_ticks)|0;
            );
            (sd_playstate[2*npos+1] >> (sdpos-1))&1 ? (// previous sd step was enabled
              midisend(cursplpos,0x80+chan[npos],npos+basenote+trans[npos]); // note-off previous subdiv note
            );
            (sd_playstate[2*npos+1] >> (sdpos))&1 ? (// current sd step is enabled
              playvelo=getSubdivVelo(npos, beatpos, sdpos);
              midisend(cursplpos,0x90+chan[npos],npos+basenote+trans[npos] + playvelo*256); // note-on subdiv note
            );
            sdpos==sd_playstate[2*npos] ? sd_playstate[2*npos]=0;
          );
        );
      );
      npos+=1;
      noneed*=0.5;
      ltiestate_t*=0.5;
      (a*=0.5) >=1 || noneed >= 1;
    );
    ticks+=1;
    ltickpos=tickpos;
  );
  
  beatpos != lbeatpos || !(play_state_i&1) ? (
    pat_trigger_pending ? (
      pat_trigger_pending=0;
      change_pattern(pat_trigger_num);
      sliderchange(slider1=p);
      sliderchange(slider3=listlength);      
    );
    pattrans_trigger_pending ? (
      pattrans_trigger_pending=0;
      all_playing_notes_off();
      basenote=pattrans_trigger_note;
      sliderchange(slider2=basenote);
      refresh_tb=1;
      ltiestate = 0; // clear all note ties
    );
    
    // Send CC
    (play_state_i&1) ? (
      i_blk=0;
      ccvalues = cclist[beatpos];
      loop(numccs,
        val = getCc(ccvalues, i_blk);
        val & 0x80 && (cc_type[i_blk] < 127)? ( // event enabled and not special type
          midisend(cursplpos, 0xB0 + cc_chan[i_blk], cc_type[i_blk] | ((val&0x7F) << 8));
        );
        i_blk+=1;
      );
    );
    
    (play_state_i&1) ? (
      a = notelist[beatpos];
      // Step probability
      rand_a_play=1;
      cc_type[0] == 127 ? (
        val = getCc(cclist[beatpos], 0);
        (val&0x80) && (rand(127) > val&0x7F) ? rand_a_play=0;
      );
    ) : a = 0;
    
    // a &= recmask; // - multiply for bitmask safety?
                   //stops notes more than 32 above basenote being transmitted
    !(play_state_i&1) ? (
      ltiestate = 0; // clear all note ties if transport is stopped
      memset(sd_playstate, 0, max_numnotes*2); // clear all subdiv states
    );
    npos=0;
    swing_beat = !((beatpos-1) % 2);
    noneed=noteonstate;
    noteonstate=0;
    tiestate=0;
    swingtiestate=0;
    swingstate=0;
    ticks = 0;
    while(
      (noneed&1) && !(ltiestate&1) ? (
         midisend(cursplpos,0x80+chan[npos],npos+basenote+trans[npos]); // send note off
      );
      (a&1) && (rand_a_play || chan[npos]!=cc_chan[0] || (ltiestate&1))? (
        sdnum=getSubdivNum(npos*listlength+beatpos);
        //(swing_beat && swing > 0 && sdnum == 0) || (swing == 0 && getStart(npos*listlength+beatpos) > 0) ? ( 

        sdnum == 0 ? ( // not subdiv
          (swing_beat && swing > 0) || (swing == 0 && getStart(npos*listlength+beatpos) > 0) ? ( 
            swingstate+=2^npos; // flag a swung or start offseted note to be played later
            (notetie[beatpos] & 2^npos) ? swingtiestate+=2^npos;
            (ltiestate&1) ? tiestate+=2^npos; // keep the tiestate
          ) : ( //normal non-swung note playback

            playvelo=getVelo(npos*listlength+beatpos);
            is_midi_val(note_to_send = npos+basenote+trans[npos]) ? (
              !(ltiestate&1) ? ( // send new note only when there is no tie from previous note
                midisend(cursplpos,0x90+chan[npos],note_to_send + playvelo*256); // send note on
              );
              noteonstate+=2^npos;
              (notetie[beatpos] & 2^npos) && (beatpos < listlength-1) ? tiestate+=2^npos; // Set tie state if tie enabled and not last note of sequence
            );
          );       
        ) : ( // subdiv note

          sd_playstate[2*npos]=sdnum;
          sd_playstate[2*npos+1]=getSubdivNotelist(npos, beatpos); // sd note enable bits
          (swing_beat && swing > 0) ? ( 
            swingstate+=2^npos; // flag a swung note to be played later
          ) : (
            sd_playstate[2*npos+1]&1 ? (// first sd step is enabled
              playvelo=getSubdivVelo(npos, beatpos, 0);
              midisend(cursplpos,0x90+chan[npos],npos+basenote+trans[npos] + playvelo*256); // note-on subdiv note
            );
            noteonstate+=2^npos;    
          );
        );
      );
      npos+=1;
      noneed*=0.5;
      ltiestate*=0.5;
      (a*=0.5) >=1 || noneed >= 1;
    );
    lbeatpos=beatpos;
    recmask = 0xFFFFFFFF;
    ltiestate=tiestate;
  );
  cursplpos += 1;
  curbeatpos += dbeatpos;
  beat_position_int+=dbeatpos;
);

midi_buf_len=i_buf;
i_buf=0;
loop(midi_buf_len,
  offset=buf_offset[i_buf];
  msg1=buf_msg1[i_buf];
  msg23=buf_msg23[i_buf];
  s = msg1&0xF0;
  n=msg23&0x7F;        
  npos=n-basenote;
  (play_state_i==5) ? ( // Recording
    vel=(msg23/256)|0;
    s==0x90 && vel > 0 ? ( // Note-On  
      (npos >= 0 && npos < numnotes) ? (
        mask = 2^npos;
        ticks > (tpb/2) ? ( // Quantize to nearest beatpos 
          bp = (beatpos+1) % listlength; 
          chain > 0 && p <= chain && bp == 0 ? (
            chain_rec_to_next = 1;
          );
        ) : ( 
          bp = beatpos; 
        ); 
        chain_rec_to_next ? ( // record note to next pattern in chain
          rec_p = p+1 % chain;
          rec_p == 0 ? (
            rec_p_notelist=notelist_base;
            rec_p_velolist=velolist_base;
          ) : (
            rec_p_notelist=notelist+listlength;
            rec_p_velolist=velolist+listlength*max_numnotes;
          );
          nlm=(rec_p_notelist[bp] & mask);
          nlm==0 ? ( 
            rec_p_notelist[bp]+=mask; // Record new note
          );
          // Overwrite velocity
          rec_velpos=npos*listlength_pat[rec_p]+bp;
          rec_p_velolist[rec_velpos] &= 0xFFFFFF80; //clear old velocity
          rec_p_velolist[rec_velpos] += vel; //set new velocity
        ) : (
          nlm=(notelist[bp] & mask);
          nlm==0 ? ( 
            notelist[bp]+=mask; // Record new note
          );
          setVelo(npos*listlength+bp, vel); // Overwrite velocity
        );
        recmask -= mask;
        chain_rec_to_next = 0;
      );
    );
  );
  // Transpose notes (Only in in Drum map mode)
  (s == 0x90 || s == 0x80) && npos >= 0 && npos < numnotes ? ( // note-on or note-off, and within visible grid
    is_midi_val(note_to_send = npos+basenote+trans[npos]) ? (
      msg23 = msg23-n+note_to_send; // transpose note
      msg1 = (msg1&0xF0) + chan[npos]; // remap MIDI channel
      midisend(offset,msg1,msg23);
    );
  ) : ( // received note is not within visible grid, or some other event received. -> send unmodified
    midisend(offset,msg1,msg23);
  );
  i_buf+=1;
);

@sample

panic_go ?
(// midi panic to stop all notes
  c = 0;
  while(
    midisend(0, $xE0+c, $x00, $x40);
    midisend(0, $xB0+c, $x7B, $x00);
    c+=1;
    c < 16; 
  );
  panic_go = 0;
);

// for drums set:: @gfx 622+pkw 400+tbh, numnotes = 16, env 1 set to prob?
@gfx 674+pkw 580+tbh

recalc_elh || lgfx_h != gfx_h? (
  recalc_elh=0;
  elh=(gfx_h - tbh) * elh_percent|0;
  elh < el_divh ? elh = el_divh; //keep lane divider always visible
  grid_bottom=gfx_h - elh;
  refresh_pianokeys=1;
  refresh_cc_controls=1;
);
  
function draw_button(x, y)
(
  color(1);
  gfx_x=x+2;gfx_y=y+1;
  gfx_rectto(gfx_x+busp-2,y+tbrh-1);
);

function draw_button_highlight(x, y)
(
  color(2);
  gfx_x=x+2;gfx_y=y+1;
  gfx_rectto(gfx_x+busp-2,y+tbrh-1);
);

function draw_pat_button(patnum, x, y, hl)
(
  patnum = patnum+(pat_shift*48);
  // Draw pattern button background
  chain > 0 && chain >= patnum ? (
    color(3);
  ) : (
    color(1);
  );
  gfx_x=x*busp+2;gfx_y=y+1;
  gfx_rectto(gfx_x+busp-2, y+tbrh-1);
  
  //Draw chain link
  chain >patnum ? (
  color(3);
    gfx_y=y+tbrh/2-3;
    gfx_rectto(gfx_x+2,gfx_y+6);
  );
  
  // Draw pattern button number
  patnum == p ? (
    color(4);
  ) : has_notes[patnum] ? (
    color(5);
  ) : (
    color(6);
  );
  gfx_x=x*busp+5; gfx_y=(y+tbrh/2-4)|0;
  patnum+1 < 10 ? gfx_x+=4; // shift single digit number right by half digit width
  gfx_drawnumber(patnum+1, 0);
  
  hl ? draw_button_highlight(x*busp,y);
);

function draw_mode_button(x,y,hl)
(
  draw_button(x,y);
  color(37);
  gfx_x=x+4; gfx_y=y+5;
  mode == 0 ? (
    gfx_printf("PR");
  ) : (
    gfx_printf("DM");
  );
  hl ? draw_button_highlight(x,y);
);

function draw_patshift_button(x,y,hl)
(
  draw_button(x,y);
  color(37);
  gfx_x=x+4; gfx_y=y+5;
  patternmode==0?(pat_shift == 0 ? (
    gfx_printf("++");
  ) : (
    gfx_printf("--");
  ););
  hl ? draw_button_highlight(x,y);
);

function draw_panic_button(x,y,hl)
(
  draw_button(x,y);
  color(37);
  gfx_x=x+8.5; gfx_y=y+5;
  gfx_printf("!");
  gfx_circle(x+12,y+8.5,6); 
  gfx_circle(x+12,y+8.5,6.5); 
  hl ? draw_button_highlight(x,y);
);

function draw_startmode_button(x,y,hl)
(
  draw_button(x,y);
  color(37);
  gfx_x=x+4; gfx_y=y+5;
  play_before_start == 0 ? (
    gfx_drawchar($' ');
    gfx_x-=3;
    gfx_drawchar($'|');
    gfx_x-=2;
    gfx_drawchar($'>');
  ) : (
    gfx_drawchar($'>');
    gfx_x-=3;
    gfx_drawchar($'|');
    gfx_x-=2;
    gfx_drawchar($'>');
  );
  hl ? draw_button_highlight(x,y);
);

function draw_position_display(x, y, hl, value)
(
  // Draw edges
  color(1);
  gfx_x=x+2;gfx_y=y+1;
  gfx_lineto(gfx_x+(busp*2-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y+(tbrh-2-1), 0);
  gfx_lineto(gfx_x-(busp*2-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y-(tbrh-2-1), 0);
  
  // Draw value
  color(38);
  gfx_x=x+6; gfx_y=y+5;
  value < 0 && abs(value) >= 100 ? (
    gfx_printf(" --- ");
  ) : value < 0 && abs(value) >= 10 ? (
    gfx_drawnumber(value,1);
  ) : value < 0 ? (
    gfx_drawnumber(value,2);
  ) : value < 10 ? (
    gfx_drawnumber(value,3);
  ) : value < 100 ? (
    gfx_drawnumber(value,2);
  ) : value < 1000 ? (
    gfx_drawnumber(value,1);
  ) : (
    gfx_x+=4;
    gfx_drawnumber(value,0);
  );
  hl ? (
    color(2);
    gfx_x=x+2;gfx_y=y+1;
    gfx_rectto(gfx_x+(busp*2-2),gfx_y+(tbrh-2));
  );
);

function draw_slider(x,y,pos)
(
  // Draw edges
  color(1);
  gfx_x=x+2;gfx_y=y+1;
  gfx_lineto(gfx_x+(busp*6-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y+(tbrh-2-1), 0);
  gfx_lineto(gfx_x-(busp*6-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y-(tbrh-2-1), 0);
  
  // Draw horizontal control bar
  color(1); gfx_a=0.5;
  gfx_x=x+2;gfx_y=y+1;
  gfx_rectto(gfx_x+(busp*6-2-1)*pos, gfx_y+(tbrh-2-1));  
);

function draw_slider_highlight(x, y)
(
  color(2);
  gfx_x=x+2;gfx_y=y+1;
  gfx_rectto(gfx_x+(busp*6-2), gfx_y+(tbrh-2));
);

function draw_z_space_highlight(x, y, z)
(
  color(2);
  gfx_x=x+2;gfx_y=y+1;
  gfx_rectto(gfx_x+(busp*z-2), gfx_y+(tbrh-2));
);

function draw_notelen_slider(x, y, hl)
(
  draw_slider(x,y,notelen_p/100);
  color(38);
  gfx_x=x+8; gfx_y=y+5;
  gfx_printf("Note Length");
  gfx_x=x+108;
  gfx_drawnumber(notelen_p,0);
  gfx_drawchar($'%');
  hl ? draw_slider_highlight(x,y);
);

function draw_swing_slider(x, y, hl)
(
draw_slider(x,y,swing_p/100);
  color(38);
  gfx_x=x+8; gfx_y=y+5;
  gfx_printf("Swing");
  gfx_x=x+108;
  gfx_drawnumber(swing_p,0);
  gfx_drawchar($'%');
  hl ? draw_slider_highlight(x,y);
);

function draw_patternlen_slider(x, y, hl)
(
  draw_slider(x,y,listlength/max_seqlength);
  color(38);
  gfx_x=x+8; gfx_y=y+5;
  gfx_printf("Seq Length");
  gfx_x=x+108;
  gfx_drawnumber(listlength,0);
  hl ? draw_slider_highlight(x,y);
);

function draw_rate_slider(x, y, hl)
(
  draw_slider(x,y,rate/4);
  color(38);
  gfx_x=x+8; gfx_y=y+5;
  gfx_printf("Rate");
  rate > 0.125 ? (
    gfx_x=x+108;
    gfx_drawnumber(rate,2);
  ) :(
    gfx_x=x+100;
    gfx_drawnumber(rate,3);
  );
  hl ? draw_slider_highlight(x,y);
);

function draw_numbeats_slider(x, y, hl)
(
  draw_slider(x,y,steps_per_beat/16);
  color(38);
  gfx_x=x+8; gfx_y=y+5;
  gfx_printf("Steps/Beat");
  gfx_x=x+108;
  gfx_drawnumber(steps_per_beat,0);
  hl ? draw_slider_highlight(x,y);
);

function draw_colors_button(x,y,hl)
(
  // Draw edges
  color(1);
  gfx_x=x+2;gfx_y=y+1;
  gfx_lineto(gfx_x+(busp*4-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y+(tbrh-2-1), 0);
  gfx_lineto(gfx_x-(busp*4-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y-(tbrh-2-1), 0);  
  // Draw label:
  color(38);
  gfx_y=y+5;
  gfx_x=x+6;
  gfx_printf(colorschemename);
  hl ? draw_z_space_highlight(x,y,4);
);

function draw_midimode_button(x,y,hl)
(
  // Draw edges
  color(1);
  gfx_x=x+2;gfx_y=y+1;
  gfx_lineto(gfx_x+(busp*6-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y+(tbrh-2-1), 0);
  gfx_lineto(gfx_x-(busp*6-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y-(tbrh-2-1), 0);  
  // Draw label:
  color(38);
  gfx_y=y+6;
  gfx_x=x+9;
  midi_trigger==0?label="Midi Off";
  midi_trigger==1?label="Pattern Change";
  midi_trigger==2?label="Transpose";
  midi_trigger==3?label="Resync";
  midi_trigger==4?label="Xpose + Resync";
  midi_trigger==5?label="Resync Quantized";
  midi_trigger==6?label="Xpose + Resync Q";		
  gfx_printf(label);
  hl ? draw_slider_highlight(x,y);
);

function draw_pattmode_button(x,y,hl)
(
  // Draw edges
  color(1);
  gfx_x=x+2;gfx_y=y+1;
  gfx_lineto(gfx_x+(busp*8-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y+(tbrh-2-1), 0);
  gfx_lineto(gfx_x-(busp*8-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y-(tbrh-2-1), 0);  
  // Draw label:
  color(38);
  gfx_y=y+6;
  gfx_x=x+9;
  patternmode==0?label="96 Patterns/64 Steps";
  patternmode==1?label="48 Patterns/128 Steps";
  gfx_printf(label);
  gfx_y=y+6;
  gfx_x=x+195;
  label2="<< Select 64/128 step mode: CLEARS ALL PATTERNS! Save First.";
  gfx_printf(label2);
  hl ? draw_z_space_highlight(x,y,8);
);
function draw_settings_button(x,y,hl)
(
  // Draw edges
  color(1);
  gfx_x=x+2;gfx_y=y+1;
  gfx_lineto(gfx_x+(busp*4-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y+(tbrh-2-1), 0);
  gfx_lineto(gfx_x-(busp*4-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y-(tbrh-2-1), 0);  
  // Draw label:
  color(38);
  gfx_y=y+5;
  gfx_x=x+6;
  gfx_printf("Settings");
  hl ? draw_z_space_highlight(x,y,4);
);

function draw_velo_display(x,y,hl)
(
  // Draw edges
  color(1);
  gfx_x=x+2;gfx_y=y+1;
  gfx_lineto(gfx_x+(busp*2-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y+(tbrh-2-1), 0);
  gfx_lineto(gfx_x-(busp*2-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y-(tbrh-2-1), 0);
  
  // Draw label:
  color(38);
  gfx_y=y+5;
  gfx_x=x+6;
  gfx_printf("V:");
  
  // Draw value
  lastvelo < 10 ? (
    gfx_x=x+22+2*8;
  ) : lastvelo < 100 ? (
    gfx_x=x+22+8;
  ) : (
    gfx_x=x+22;
  );
  gfx_drawnumber(lastvelo,0);
  drawnvelo=lastvelo;
  hl ? (
    color(2);
    gfx_x=x+2;gfx_y=y+1;
    gfx_rectto(gfx_x+(busp*2-2-1),gfx_y+(tbrh-2-1));
  );
);

function draw_move_button(x,y,hl)
(
  // Draw edges
  color(1);
  gfx_x=x+2;gfx_y=y+1;
  gfx_lineto(gfx_x+(busp*2-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y+(tbrh-2-1), 0);
  gfx_lineto(gfx_x-(busp*2-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y-(tbrh-2-1), 0);
  
  // Draw label:
  color(38);
  gfx_y=y+5;
  gfx_x=x+10;
  gfx_printf("Move");
 
  hl ? (
    color(2);
    gfx_x=x+2;gfx_y=y+1;
    gfx_rectto(gfx_x+(busp*2-2-1),gfx_y+(tbrh-2-1));
  );
);

function isSharp(note) local(octavestart)
(
  octavestart = note % 12; 
  octavestart == 1 || octavestart == 3 || octavestart == 6 || octavestart == 8 || octavestart == 10 ? (
    1;
  ) : (
    0;
  );
);

function getNotename(note, default_names)
( 
  notenames[note]==0 || default_names ? ( // empty notename -> use default note names
    #name_str=nn[note % 12];
    isSharp(note) ? (
      #name_str+="#";
    ) : (
      #name_str+=" ";
    );
    notename_digit=floor((note-12)/12 + octave_offset);  
    strcat(#name_str, sprintf(#, "%{notename_digit}d"));
  ) : ( // use notename extracted from file
    #name_str=notenames[note];
  );
);

function draw_trigstart_display(x,y,hl)
(
  // Draw edges
  color(1);
  gfx_x=x+2;gfx_y=y+1;
  gfx_lineto(gfx_x+(busp*4-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y+(tbrh-2-1), 0);
  gfx_lineto(gfx_x-(busp*4-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y-(tbrh-2-1), 0);
  
  // Print label and value
  color(38);
  gfx_y=y+5;
  gfx_x=x+6;
  gfx_printf("Trig: ");
  gfx_printf(getNotename(trig_note_start, 1));
  
  hl ? (
    color(2);
    gfx_x=x+2;gfx_y=y+1;
    gfx_rectto(gfx_x+(busp*2-2-1),gfx_y+(tbrh-2-1));
  );
);

function draw_basenote_display(x,y,hl)
(
  // Draw edges
  color(1);
  gfx_x=x+2;gfx_y=y+1;
  gfx_lineto(gfx_x+(busp*4-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y+(tbrh-2-1), 0);
  gfx_lineto(gfx_x-(busp*4-2-1), gfx_y, 0);
  gfx_lineto(gfx_x, gfx_y-(tbrh-2-1), 0);
  
  // Print label and value
  color(38);
  gfx_y=y+5;
  gfx_x=x+6;
  gfx_printf("Base: ");
  gfx_printf(getNotename(basenote, 1));
  
  hl ? (
    color(2);
    gfx_x=x+2;gfx_y=y+1;
    gfx_rectto(gfx_x+(busp*2-2-1),gfx_y+(tbrh-2-1));
  );
);

function draw_velocity(x,y,hl) 
(
  // Draw default velocity value
  color(31); gfx_x=gfx_y=0;
  gfx_x=x; gfx_y=y;
  gfx_rectto(gfx_x+(busp*2-2-1),gfx_y+(tbrh-4-1));
  draw_velo_display(x,y,0);
  hl ? (
    color(2);
    gfx_x=x+2;gfx_y=y+1;
    gfx_rectto(gfx_x+(busp*2-2-1),gfx_y+(tbrh-2-1));
  );
);

function draw_drum_note(note) local(notepos, nt, trans, half_rh, scaled_tr, offset, name)
(
  notepos = numnotes-1-note;
  nt = basenote+note+notetranspose[note];
  name=#;
  name=getNotename(nt, 0);
  // Erase previous text by drawing black text over
  color(7);
  gfx_x=chw+4.0; gfx_y=tbh+notepos*row_height+row_height/2-3;
  gfx_printf(name); 
  gfx_x=4.0;
  rowchannel[note]+1 < 10 ? gfx_x=gfx_x+3.0;
  gfx_drawnumber(rowchannel[note]+1, 0);
  
  color(10);
  gfx_x=2.0; gfx_y=tbh+notepos*row_height+1;
  gfx_rectto(chw-3, gfx_y+row_height-2);
  !isSharp(basenote+note) ? ( // draw faint white key background
    color(8);
    gfx_x=chw; gfx_y=tbh+notepos*row_height+1;
    gfx_rectto(pkw-3, gfx_y+row_height-2);
  );
  color(9);
  gfx_x=4.0; gfx_y=tbh+notepos*row_height+row_height/2-3;
  rowchannel[note]+1 < 10 ? gfx_x=gfx_x+3.0;
  gfx_drawnumber(rowchannel[note]+1, 0);
  gfx_x=chw+4.0;
  
  color(4);
  notenames[nt]==0 ? gfx_a=0.3 : gfx_a=0.6; 
  gfx_printf(name);
  
  //Draw transpose indicator
  trans = notetranspose[note];
  trans != 0 ? (     
    half_rh = row_height/2|0;
    color(11);
    gfx_x=chw+0.0; gfx_y=tbh+(notepos*row_height+half_rh)|0;
    
    scaled_tr = trans/8;
    offset = (half_rh-1)*scaled_tr;
    offset > 0 && offset < 1 ? offset=1;
    offset < 0 && offset > -1 ? offset=-1;
    offset=offset|0;
    offset > half_rh-1 ? offset = half_rh-1;
    offset < (-half_rh)+1 ? offset = (-half_rh)+1;
    gfx_rectto(gfx_x+3, gfx_y - offset);
  );
);

function draw_tb_pianokeys(x, y) local(note, w, gap, in_range, in_trig_range )
( 
  gfx_x=x+2;gfx_y=y+1;
  w_scale=-1;
  while( 
    w_scale+=1;
    gfx_w-128 > 483+w_scale*128;
  );
  
  note=0;
  loop(128,
    note >= basenote && note < basenote+numnotes ? in_range=1 : in_range=0;
    note >= trig_note_start && note < trig_note_start+npatterns ? in_trig_range=1 : in_trig_range=0;
    isSharp(note) ? (
     color(7); 
      in_range ? (color(12););
      in_trig_range ? (!in_range ? color(13););
      w=3;
      gap=0;
    ) : (
      color(4);
      in_range ? (color(14););
      in_trig_range ? (color(15););
      w=4;
      !isSharp(note+1) ? gap=1 : gap=0;
    );
    gfx_rectto(gfx_x+w+w_scale,gfx_y+tbrh-2);
    gfx_x+=gap; gfx_y=y+1;
    note+=1;
  );
  tb_pianokey_end=10*(7*4+5*3+12*w_scale+2)+5*4+3*3+8*w_scale+1;
);

function clear_pattern(pat) local(i, notelist, velolist, notetie, cclist)
(
  notelist=0;
  velolist=velolist_base;
  i=0;
  loop(pat,
    notelist+=listlength_pat[i];
    velolist+=max_numnotes*listlength_pat[i];
    i+=1;
  );
  notetie=notetie_base+notelist;
  cclist=cclist_base+notelist;
  notelist=notelist_base+notelist;
  memset(notelist, 0, listlength_pat[pat]);
  memset(notetie, 0, listlength_pat[pat]);
  memset(cclist, 0, listlength_pat[pat]);
  memset(velolist, 0, listlength_pat[pat]*max_numnotes);
  memset(sparebuf + pat*1024, 0, max_seqlength);
  memset(sparebuf_tie + pat*1024, 0, max_seqlength);
  memset(sparebuf_cc + pat*1024, 0, max_seqlength);
  memset(sparebuf2 + pat*max_numnotes*max_seqlength, 0, max_numnotes*max_seqlength);
);

function draw_pianokeys() local(notepos, octavestart)
(
  // Erase old keys
  //color(7); gfx_y=tbh-1;

  color(7); gfx_x=0; gfx_y=tbh-1;  
  gfx_rectto(pkw,grid_bottom);
  
  // Draw new keys
  notepos=0;
  loop(numnotes,
    octavestart = (basenote+(numnotes-1-notepos)) % 12;
    // Draw piano keys
    row_height >= 2 ? (
      mode == 0 && !(octavestart == 1 || octavestart == 3 || octavestart == 6 || octavestart == 8 || octavestart == 10) ? (
        color(4);
        gfx_x=2.0; gfx_y=tbh+notepos*row_height+1;
        gfx_rectto(pkw-3, gfx_y+row_height-2);
      );
    );
    // Draw note names
    row_height >= 4 ? (
      mode == 0 ? (
        octavestart == 0 ? (
          color(16);
          gfx_x=chw+4.0; gfx_y=tbh+notepos*row_height+row_height/2-3;
          gfx_drawchar($'C');
          gfx_drawnumber(((basenote+(numnotes-1-notepos)) / 12)-1+octave_offset,0);
        );
      ) : (
        draw_drum_note(numnotes-1-notepos);
      );
    );
    notepos+=1;
  );
);

function getEnvColor(env)
(
  env == 0 ? (color(27););
  env == 1 ? (color(28););
  env == 2 ? (color(29););
  env == 3 ? (color(30););
);

function setEnvColor(env)
(
  env == cc_to_adjust ? (
    getEnvColor(env);
    gfx_a=1;
  ):(
  setcolor(76,76,76,127);
  );
);

function setCcControlColor(env)
(
  getEnvColor(env);
  gfx_a=1;
  env != cc_to_adjust ? ( 
    gfx_r*=0.3; gfx_g*=0.3; gfx_b*=0.3;
  );
);

function draw_cc_controls() local(cc_control_top, cc_control_height, ccpos)
(
  cc_control_top=grid_bottom+el_divh;
  
  // Erase old cc controls
  color(7); gfx_y=cc_control_top; gfx_x=0;
  gfx_rectto(pkw,gfx_h);
  
  cc_control_height=(elh-el_divh)/numccs;
  // Draw new cc controls
  cc_control_height >= 2 ? (
    ccpos=0;
    loop(numccs,
      setCcControlColor(ccpos);
      mode == 1 ? (
        gfx_x=2.0; gfx_y=cc_control_top+ccpos*cc_control_height+1;
        gfx_rectto(chw-3, gfx_y+cc_control_height-2);
        color(35);
        ccpos == cc_to_adjust ? gfx_a=154/255 : gfx_a=77/255;
        gfx_x=4.0; gfx_y=cc_control_top+ccpos*cc_control_height+cc_control_height/2-3;
        cc_chan[ccpos]+1 < 10 ? gfx_x=gfx_x+3.0;
        gfx_drawnumber(cc_chan[ccpos]+1, 0);
      );
      setCcControlColor(ccpos);
      gfx_x=chw; gfx_y=cc_control_top+ccpos*cc_control_height+1;
      gfx_rectto(pkw-3, gfx_y+cc_control_height-2);
      color(35);
      ccpos == cc_to_adjust ? gfx_a=154/255 : gfx_a=77/255;
      gfx_x=chw+4.0; gfx_y=cc_control_top+ccpos*cc_control_height+cc_control_height/2-3; 
      elh >=32 ? (
        cc_type[ccpos] == 1 ? (
          gfx_printf("Mod");
        ) : cc_type[ccpos] == 7 ? (
          gfx_printf("Vol");
        ) : cc_type[ccpos] == 10 ? (
          gfx_printf("Pan");
        ) : cc_type[ccpos] == 11 ? (
          gfx_printf("Exp");
        ) : cc_type[ccpos] == 64 ? (
          gfx_printf("HPd");
        ) : cc_type[ccpos] == 127 && ccpos==0 ? (
          gfx_printf("Prb");
        ) : (
          cc_type[ccpos] < 10 ? gfx_x=gfx_x+3.0;
          gfx_drawnumber(cc_type[ccpos], 0);
        );
      );
      ccpos+=1;
    );
  );
);

function draw_velobar(x, y, velo, note_w, note_h) local(tip_y, barwidth) // x, y is the bottom left corner of bar
(
  tip_y = 0.95*row_height*velo/128;
  tip_y < 1.0 ? tip_y = 1.0;
  tip_y > note_h ? tip_y = note_h;
  tip_y=tip_y|0;
  color(36);
  velobarwidth > note_w ? barwidth = note_w : barwidth = velobarwidth;
  gfx_x=x; gfx_y=y; 
  gfx_rectto(gfx_x + barwidth, gfx_y - tip_y);
);

function veloadjust_row(y, x, sd_x, veloadj) local(mask, i, sd_nl, sd_i, newvelo)
(
  lastnote_changed_x=x;
  mask = 2^y;
  i=0;
  loop(listlength,
    (notelist[i] & mask) ? (
      getSubdivNum(y*listlength+i) ? ( // subdiv note
        sd_nl = getSubdivNotelist(y, i);
        sd_nl ? ( // has enabled subdiv notes
          sd_i=0;
          loop(max_subdivs,
            sd_nl&(2^sd_i) ? (
              newvelo = getSubdivVelo(y, i, sd_i) + veloadj;
              newvelo < 1 ? newvelo=1;
              newvelo > 127 ? newvelo=127;
              setSubdivVelo(y, i, sd_i, newvelo); // Edit velocity
              i == x && sd_i == sd_x ? (lastnote_changed_velo = newvelo; sd_editval_show=1;);
            );
            sd_i+=1;
          );
        );
      ) : ( // not subdiv
        newvelo = getVelo(y*listlength+i) + veloadj;
        newvelo < 1 ? newvelo=1;
        newvelo > 127 ? newvelo=127;
        setVelo(y*listlength+i, newvelo);
        i == x ? lastnote_changed_velo = newvelo;
      );
    );
    i+=1;
  );
);
            
row_height = (grid_bottom-tbh) / numnotes;

lgfx_w != gfx_w || lgfx_h != gfx_h || llistlength != listlength || lnumnotes != numnotes || lbasenote != basenote || lrateadj != rateadj || lmode != mode ? (
    
  lgfx_w = gfx_w; lgfx_h = gfx_h;
  llistlength = listlength;
  lnumnotes = numnotes;
  lbasenote = basenote;
  lrateadj = rateadj;
  lmode = mode;
  color(7); gfx_y=tbh; 
  gfx_rectto(gfx_w,gfx_h);
  
  draw_pianokeys();
  draw_cc_controls();
  
  velobarwidth = scale_velobarwidth(listlength);
  
  // Precalc values for bar line drawing
  nbars = listlength/rateadj/4/(ts_num_l/ts_denom_l);
  barlen = (gfx_w - pkw)/nbars;

  // Precalc note square width
  note_w = (gfx_w - pkw)/listlength;
);

lnotenames_file != notenames_file && mode==1 ? (
  lnotenames_file=notenames_file;
  
  memset(notenames, 0, 128); // clear old note names
  max_notename_length=4; // reset to default
  notename_str = #;
  nn_filename = #;
  // handle = file_open("seqbaby_data/GM Kit.txt");
  strcpy_fromslider(nn_filename, slider15);
  !((handle = file_open(nn_filename)) < 0) ? ( // file_open successful
    nn_name=1; // collect note names to string slots starting from slot 1, slot 0 is always empty string
    notename_name = #;
    while(
      str_len = file_string(handle,notename_str);
      match("%-3{notename_note}d\t%{notename_name}s",notename_str) ? (
        notename_note >= 0 && notename_note <= 127 ? (
          strncpy(nn_name, notename_name, strlen(notename_name)-2);
          notenames[notename_note]=nn_name;
          strlen(nn_name) > max_notename_length ? max_notename_length = strlen(nn_name);
          nn_name+=1;         
        );
      );
      str_len > 0;
    );
    file_close(handle);
  );
  pianow=max_notename_length*8+8;
  pkw=chw+pianow;
  barlen = (gfx_w - pkw)/nbars; // Update barlen for bar line drawing
  note_w = (gfx_w - pkw)/listlength; // Update note square width
  draw_pianokeys();
  draw_cc_controls();
);

refresh_pianokeys ? (
  refresh_pianokeys=0;
  draw_pianokeys();
);

y_from_top=((mouse_y-tbh) / (grid_bottom-tbh))*numnotes;
y = numnotes - 1 - (y_from_top|0);
y_acc = numnotes - y_from_top;
mouse_y < tbh ? y+=1;

((mouse_y >= tbh && mouse_y < grid_bottom) || mstate&ms_grid || mstate&ms_pk) && !(mstate&ms_tb) && !(mstate&ms_el)? ( // Mouse below toolbar and above env lane or clicking/dragging in progress

  // Highlight current piano key
  (y != lhl && mouse_x >= 0 && mouse_x < gfx_w && mouse_y >= tbh && mouse_y < grid_bottom) && (mstate == 0 || ms_sub_drawfreehand || mstate == ms_preview) ? (
  
  // Remove previous highlight
    ypos = (numnotes-1-lhl)*row_height;
    octavestart = (basenote+lhl) % 12;
    mode == 0 && (octavestart == 0 || octavestart == 2 || octavestart == 4 || octavestart == 5 || octavestart == 7 || octavestart == 9 || octavestart == 11) ? (   
      color(4); // white key
      gfx_x=2.0; gfx_y=tbh+ypos+1;
      gfx_rectto(pkw-3, gfx_y+row_height-2);
    ) : (
      color(7); // black key
      gfx_x=0.0; gfx_y=tbh+ypos+1;
      gfx_rectto(pkw-3, gfx_y+row_height-2);
    );
    mode == 0 && octavestart == 0 ? (
      color(16);
      gfx_x=chw+4.0; gfx_y=tbh+ypos+row_height/2-3;
      gfx_drawchar($'C');
      gfx_drawnumber(((basenote+lhl) / 12)-1+octave_offset,0);
    );
    mode == 1 ? (
      draw_drum_note(lhl);
    );
    
    lhl = y;

    // Draw new highlight
    ypos = (numnotes-1-y)*row_height;
    color(2);
    gfx_x=2.0; gfx_y=tbh+ypos+1;
    gfx_rectto(pkw-3, gfx_y+row_height-2);
  notehl = 1;
  );

  // Right click
  (mouse_cap==2) ? ( 
    x = (((mouse_x - pkw)/(gfx_w - pkw))*listlength)|0;
    y >= 0 && y < numnotes ? (
      mstate==0 || mstate==ms_preview ? ( // Click / Button down or dragging
        mstate==0 ? (
          last_x=last_y=last_sd_x=-1;
        );
        mstate=ms_preview;
     
        x >= 0 && x < listlength ? (
          sd_edit_num = getSubdivNum(y*listlength+x);
          sd_edit_num ? ( // subdiv note
            sd_edit_nl = getSubdivNotelist(y, x);
            sd_x = ((sd_edit_num+1)*((mouse_x - pkw) - x*note_w)/note_w)|0;
            sd_edit_nl&(2^sd_x) && (last_x != x || last_sd_x != sd_x || last_y != y ) ? (
              new_note_at_mousepos=1;
            ) : (
              new_note_at_mousepos=0;
            );
          ) : (notelist[x] & 2^y) && (last_x != x || last_y != y) ? ( // not subdiv
            new_note_at_mousepos=1;
          ) : (
            new_note_at_mousepos=0;
          );
        );

        x >= 0 && x < listlength && new_note_at_mousepos ? 
        (
          previewvelo=lastvelo;
          sd_edit_num ? ( // subdiv note
              previewvelo = getSubdivVelo(y, x, sd_x);
              lastvelo=previewvelo;
          ) : ( // not subdiv
            previewvelo=getVelo(y*listlength+x);
            lastvelo=previewvelo;
          );
          lastpreviewsel>=0 ? want_previewoff=1;
          want_preview=lastpreviewsel=y; 
        ) : y != lastpreviewsel ? (
          previewvelo=lastvelo;
          lastpreviewsel>=0 ? want_previewoff=1;
          want_preview=lastpreviewsel=y; 
        );
        last_x = x;
        last_y = y;
        last_sd_x = sd_x;
      );
    );
  ):(
    want_previewoff=1;
    lastpreviewsel=-1;
  );

  (mouse_x > pkw || mstate == ms_startoffsetedit || mstate == ms_startoffseteditrow) && mstate != ms_rowtranpose ? (
    // Left click or Shift + Ctrl + Alt + Left click
    (mouse_cap==1) || (mouse_cap==(1|4|8|16)) ? ( 
        x_acc=((mouse_x - pkw)/(gfx_w - pkw))*listlength;
        x = x_acc|0;
        x >= 0 && x < listlength && y >= 0 && y < numnotes ? (
          mstate==0 ? (
            ylock = y;
            mask = 2^y;
            sd_edit_num = getSubdivNum(ylock*listlength+x);
            
            sd_edit_num ? ( // subdiv note
              sd_edit_nl = getSubdivNotelist(ylock, x);
              sd_x = ((sd_edit_num+1)*((mouse_x - pkw) - x*note_w)/note_w)|0;
              mstate=sd_edit_nl&(2^sd_x) ? ms_noteerase : ms_notedraw;
              mstate==ms_notedraw ? ( // Draw note
                setSubdivNotelist(ylock, x, sd_edit_nl+2^sd_x);
                setSubdivVelo(ylock, x, sd_x, lastvelo);
              ); 
              mstate==ms_noteerase ? ( // Erase note
                setSubdivNotelist(ylock, x, sd_edit_nl-2^sd_x);
              );
            ) : ( // not subdiv
              nlm=(notelist[x] & mask);
              mstate=nlm ? ms_noteerase : ms_notedraw;
              mstate==ms_noteerase ? ( // Erase note
                notelist[x]-=mask; 
                (notetie[x] & mask) ? notetie[x]-=mask; // Erase tie
                x > 0 && (notetie[x-1] & mask) ? notetie[x-1]-=mask; // Erase tie from previous 
                setStart(y*listlength+x, 0); //Reset start offset of the erased note to 0 
              );
              mstate==ms_notedraw ? ( // Draw note
                notelist[x]+=mask; 
                setVelo(y*listlength+x, lastvelo);
              ); 
            );
            ((mouse_cap!=1)||(mode==0)) ? ms_sub_drawfreehand=1;
          ) : (         
            lcnt = max(abs(last_x-x),abs(last_y-y))|0;
            lcnt > 0 ? (
              dx = (x_acc-last_x_acc)/lcnt;
              dy = (y_acc-last_y_acc)/lcnt;
              //y_step = (abs(dy) > abs(dx));
            );
            lcnt >= 1 ? (
              loop(lcnt,
                last_x_prev=last_x_acc|0; 
                last_x_acc += dx;
                last_x_int=last_x_acc|0;
                ms_sub_drawfreehand ? (
                  last_y_acc += dy;
                  last_y_int = last_y_acc|0;
                ) : (
                  last_y_int = ylock ;
                );
				
                last_x_int != last_x_prev ? ( // Fast drawing over to note on left or right -> need draw subdivs in prev note
                  sd_edit_num_prev = getSubdivNum(last_y_int*listlength+last_x_prev);
                  
                  sd_edit_num_prev ? ( // previous was subdiv note
                    last_sd_x=sd_x;
                    dx < 0 && last_sd_x > 0 ? (
                      lcnt_sd = last_sd_x;
                      dx_sd = -1;
                    ) : dx > 0 && last_sd_x < sd_edit_num_prev ? (
                      lcnt_sd = sd_edit_num_prev-last_sd_x;
                      dx_sd = 1;
                    );
                    
                    lcnt_sd >= 1 ? (
                      loop(lcnt_sd,
                        last_sd_x += dx_sd;
                        sd_edit_nl = getSubdivNotelist(last_y_int, last_x_prev);
                        mstate==ms_noteerase ? (
                          (sd_edit_nl&(2^last_sd_x)) ? (
                            setSubdivNotelist(last_y_int, last_x_prev, sd_edit_nl-2^last_sd_x);
                          );
                        );
                        mstate==ms_notedraw ? ( // Draw note
                          !(sd_edit_nl&(2^last_sd_x)) ? (
                            setSubdivNotelist(last_y_int, last_x_prev, sd_edit_nl+2^last_sd_x);
                            setSubdivVelo(last_y_int, last_x_prev, last_sd_x, lastvelo);
                          );
                        );
                      );
                    );
                  );
                );
                
                sd_edit_num = getSubdivNum(last_y_int*listlength+last_x_int);
                
                sd_edit_num ? ( // subdiv note
                  last_x_int != last_x_prev ? (
                    dx < 0 ? sd_x=sd_edit_num+1 : sd_x=-1;
                  );
                                   
                ) : ( // not subdiv
                  mask = 2^last_y_int;
                  nlm=(notelist[last_x_int] & mask);
                  mstate==ms_noteerase && nlm ? (
                    notelist[last_x_int]-=mask; // Erase note
                    (notetie[last_x_int] & mask) ? notetie[last_x_int]-=mask; // Erase tie
                    last_x_int > 0 && (notetie[last_x_int-1] & mask) ? notetie[last_x_int-1]-=mask; // Erase tie from previous
                    setStart(last_y_int*listlength+(last_x_int), 0); //Reset start offset of the erased note to 0 
                  );
                  mstate==ms_notedraw && !nlm ? ( // Draw note
                    notelist[last_x_int]+=mask; 
                    setVelo(last_y_int*listlength+(last_x_int), lastvelo);
                  );
                );
              );
            ) : ( // lcnt == 0. Within same note  
              sd_edit_num = getSubdivNum(ylock*listlength+x);
              sd_edit_num ? ( // subdiv note
                sd_x = ((sd_edit_num+1)*((mouse_x - pkw) - x*note_w)/note_w)|0;
                lcnt_sd = abs(last_sd_x-sd_x)|0;
                lcnt_sd > 0 ? (
                  dx_sd = (sd_x-last_sd_x)/lcnt_sd;
                  //dy = (y-last_y)/lcnt_sd;
                );
                lcnt_sd >= 1 ? (
                  loop(lcnt_sd,
                    last_sd_x += dx_sd;
                    //last_y += dy;
                    ms_sub_drawfreehand==0 ? last_y_int=ylock;
                    last_x_int=(last_x|0);
                    sd_edit_nl = getSubdivNotelist(last_y_int, last_x_int);
                    mstate==ms_noteerase ? (
                      (sd_edit_nl&(2^last_sd_x)) ? (
                        setSubdivNotelist(last_y_int, last_x_int, sd_edit_nl-2^last_sd_x);
                      );
                    );
                    mstate==ms_notedraw ? ( // Draw note
                      !(sd_edit_nl&(2^last_sd_x)) ? (
                        setSubdivNotelist(last_y_int, last_x_int, sd_edit_nl+2^last_sd_x);
                        setSubdivVelo(last_y_int, last_x_int, last_sd_x, lastvelo);
                      );
                    );
                  );
                );
              );
            );
          );
          last_x = x;
          last_x_acc = x_acc;
          last_y = y; 
          last_y_acc = y_acc;
          last_sd_x = sd_x;
        );
    ) : ms_sub_drawfreehand=0;

    // Ctrl + Left click
    (mouse_cap==(1|4)) ? ( 
        x = (((mouse_x - pkw)/(gfx_w - pkw))*listlength)|0;
        x >= 0 && x < listlength ? (
          velo = (((grid_bottom - mouse_y)-y*row_height) / row_height)*127|0;
          mstate==0 ? ( // clicking
            sd_editval_show=0;
            ylock = y;
            mask = 2^y;
            sd_edit_num = getSubdivNum(ylock*listlength+x);
            sd_edit_num ? ( // subdiv note
              sd_edit_nl = getSubdivNotelist(ylock, x);
              sd_x = ((sd_edit_num+1)*((mouse_x - pkw) - x*note_w)/note_w)|0;
              mstate=sd_edit_nl&(2^sd_x) ? ms_veloedit;
              mstate==ms_veloedit ? ( 
                setSubdivVelo(ylock, x, sd_x, velo); // Edit velocity
                lastvelo=velo;
                sd_editval_show=1;
              ); 
            ) : ( // not subdiv
              nlm=(notelist[x] & mask);
              mstate = nlm ? ms_veloedit;
              nlm ? (
                setVelo(y*listlength+x, velo); // Edit velocity
                lastvelo=velo;
              );
            );
            lastnote_changed_x=x;
            
          ) : ( // dragging
            sd_editval_show=0;
            y < ylock ? velo = 1;
            y > ylock ? velo = 127;
            lcnt = max(abs(last_x-x),abs(last_y-y))|0;
            lcnt > 0 ? (
              dx = (x-last_x)/lcnt;
              dy = (y-last_y)/lcnt;
              dmoy = (mouse_y - last_mo_y)/lcnt;
            );
            lcnt >= 1 ? (
              loop(lcnt,
                last_x_prev=last_x|0; 
                last_x += dx;
                last_y += dy;
                last_mo_y += dmoy;
                last_x_int=(last_x|0);
                
                last_x_int != last_x_prev ? (
                  sd_edit_num_prev = getSubdivNum(ylock*listlength+last_x_prev);
                  
                  sd_edit_num_prev ? ( // previous was subdiv note
                    last_sd_x=sd_x;
                    dx < 0 && last_sd_x > 0 ? (
                      lcnt_sd = last_sd_x;
                      dx_sd = -1;
                    ) : dx > 0 && last_sd_x < sd_edit_num_prev ? (
                      lcnt_sd = sd_edit_num_prev-last_sd_x;
                      dx_sd = 1;
                    );
                    
                    lcnt_sd >= 1 ? (
                      loop(lcnt_sd,
                        last_sd_x += dx_sd;
                        sd_edit_nl = getSubdivNotelist(ylock, last_x_prev);
                        (sd_edit_nl&(2^last_sd_x)) ? (
                          last_y > ylock ? ( 
                            velo = 127;  
                          ) : last_y < ylock ? (
                            velo = 1;
                          ) : (
                            velo = (((grid_bottom - last_mo_y)-last_y*row_height) / row_height)*127|0;
                          );
                          setSubdivVelo(ylock, last_x_prev, last_sd_x, velo);
                        );
                      );
                    );
                  );
                );
                
                sd_edit_num = getSubdivNum(ylock*listlength+last_x_int);
                sd_edit_num ? ( // subdiv note
                  last_x_int != last_x_prev ? (
                    dx < 0 ? sd_x=sd_edit_num+1 : sd_x=-1;
                  );
                                   
                ) : ( // not subdiv
                  mask = 2^(ylock|0);
                  nlm=(notelist[last_x|0] & mask);
                  last_y > ylock ? ( 
                    velo = 127;  
                  ) : last_y < ylock ? (
                    velo = 1;
                  ) : (
                    velo = (((grid_bottom - last_mo_y)-last_y*row_height) / row_height)*127|0;
                  );
                  nlm ? (lastnote_changed_x=last_x|0; setVelo(ylock*listlength+lastnote_changed_x, velo); lastvelo=velo;);  
                );
              );

            ) : ( // lcnt == 0. Within same note  
              sd_edit_num = getSubdivNum(ylock*listlength+x);
              sd_edit_num ? ( // subdiv note
                sd_x = ((sd_edit_num+1)*((mouse_x - pkw) - x*note_w)/note_w)|0;
                lcnt_sd = abs(last_sd_x-sd_x)|0;
                lcnt_sd > 0 ? (
                  dx_sd = (sd_x-last_sd_x)/lcnt_sd;
                  dmoy = (mouse_y - last_mo_y)/lcnt_sd;
                );
                lcnt_sd >= 1 ? (
                  loop(lcnt_sd,
                    last_sd_x += dx_sd;
                    last_mo_y += dmoy;
                    last_x_int=(last_x|0);
                    sd_edit_nl = getSubdivNotelist(ylock, last_x_int);
                    (sd_edit_nl&(2^last_sd_x)) ? (
                      last_y > ylock ? ( 
                        velo = 127;  
                      ) : last_y < ylock ? (
                        velo = 1;
                      ) : (
                        velo = (((grid_bottom - last_mo_y)-last_y*row_height) / row_height)*127|0;
                      );
                      setSubdivVelo(ylock, last_x_int, last_sd_x, velo);
                      lastnote_changed_x=last_x_int;
                      lastvelo=velo;
                      sd_editval_show=1;
                    );
                    
                  );
                ) : ( // lcnt_sd == 0. Adjusting single subdiv note velocity
                  sd_edit_nl = getSubdivNotelist(ylock, last_x);
                  (sd_edit_nl&(2^sd_x)) ? (
                    last_y > ylock ? ( 
                      velo = 127;  
                    ) : last_y < ylock ? (
                      velo = 1;
                    );
                    setSubdivVelo(ylock, last_x, sd_x, velo);
                    lastnote_changed_x=last_x;
                    lastvelo=velo;
                    sd_editval_show=1;
                  );
                );
              ) : ( // not subdiv
                last_y > ylock ? ( 
                  velo = 127;  
                ) : last_y < ylock ? (
                  velo = 1;
                );
                nlm ? (lastnote_changed_x=x; setVelo(ylock*listlength+lastnote_changed_x, velo); lastvelo=velo;);
              );
            );
          );
          last_x = x;
          last_y = y; 
          last_sd_x = sd_x;
          last_mo_y = mouse_y;
        );
    );

    // Ctrl + Alt + Left click

    (mouse_cap==(1|4|16)) ? ( 
        x = (((mouse_x - pkw)/(gfx_w - pkw))*listlength)|0;
        x >= 0 && x < listlength ? (
          velo = (((grid_bottom - mouse_y)-y*row_height) / row_height)*127|0;
          mstate==0 ? ( // clicking
            ylock = y;
            mask = 2^y;
            sd_edit_num = getSubdivNum(ylock*listlength+x);
            sd_edit_num ? ( // subdiv note
              sd_edit_nl = getSubdivNotelist(ylock, x);
              sd_x = ((sd_edit_num+1)*((mouse_x - pkw) - x*note_w)/note_w)|0;
              sd_edit_nl&(2^sd_x) ? (
                mstate=ms_veloeditrow;
                veloadj = velo - getSubdivVelo(y, x, sd_x);
              );
            ) : ( // not subdiv
              nlm=(notelist[x] & mask);
              nlm ? (
                mstate = ms_veloeditrow;
                veloadj = velo - getVelo(y*listlength+x);
              );
            );
            ms_veloeditrow ? (
              veloadjust_row(y, x, sd_x, veloadj);
              lastvelo=velo;
            );
              
          ) : ( // dragging
            y < ylock ? velo = 1;
            y > ylock ? velo = 127;
            veloadj_ena = 0;
            sd_edit_num = getSubdivNum(ylock*listlength+x);
            sd_edit_num ? ( // subdiv note
              sd_edit_nl = getSubdivNotelist(ylock, x);
              sd_x = ((sd_edit_num+1)*((mouse_x - pkw) - x*note_w)/note_w)|0;
              sd_edit_nl&(2^sd_x) ? (
                veloadj = velo - getSubdivVelo(ylock, x, sd_x);
                veloadj_ena = 1;
              );
            ) : ( // not subdiv
              nlm=(notelist[x] & 2^ylock);
              nlm ? (
                veloadj = velo - getVelo(ylock*listlength+x);
                veloadj_ena = 1;
              );
            );
            veloadj_ena ? (
              veloadjust_row(ylock, x, sd_x, veloadj);
              lastvelo=velo;
            );
          ); 
        );
    );
    
    // Shift + Left click
    mouse_cap==(1|8) ? ( 
      x = (((mouse_x - pkw)/(gfx_w - pkw))*listlength)|0;
      mstate==0 ? (
        x >= 0 && x < listlength ? (
          sd_edit_num = getSubdivNum(y*listlength+x);
          !sd_edit_num ? ( // not subdiv note
            ylock = y;
            xlock = x;
            mask = 2^y;
            nlm=(notelist[x] & mask);
            mstate = nlm ? ms_startoffsetedit;
            morig = mouse_x;
            start_adj_orig = getStart(ylock*listlength+x);
            lastnote_changed_x=x;
          );
        );
      ) : (
        draglen=((mouse_x - morig))|0;
        ldraglen != draglen ? (
          ldraglen = draglen;
          nlm ? (
            new_start = start_adj_orig+draglen;
            new_start < 0 ? new_start = 0;
            new_start > tpb ? new_start = tpb;
            setStart(ylock*listlength+xlock, new_start);
          );
        );
      );
    );

    // Shift + Alt + Left click
    mouse_cap==(1|8|16) ? ( 
      x = (((mouse_x - pkw)/(gfx_w - pkw))*listlength)|0;
      mstate==0 ? (
        x >= 0 && x < listlength ? (
          sd_edit_num = getSubdivNum(y*listlength+x);
          !sd_edit_num ? ( // not subdiv note
            ylock = y;
            xlock = x;
            mask = 2^y;
            nlm=(notelist[x] & mask);
            mstate = nlm ? ms_startoffseteditrow;
            morig = mouse_x;
            i=0;
            loop(listlength,
              start_adj_orig_list[i] = getStart(ylock*listlength+i);
              i == x ? start_adj_all_value = start_adj_orig_list[i];
              i+=1;
            );
            lastnote_changed_x=x;
          );
        );
      ) : (
        draglen=((mouse_x - morig))|0;
        ldraglen != draglen ? (
          ldraglen = draglen;
          mask = 2^ylock;
          nlm ? (
            i=0;
            loop(listlength,
              (notelist[i] & mask) && !getSubdivNum(ylock*listlength+i) ? (
                new_start = start_adj_orig_list[i]+draglen;
                new_start < 0 ? new_start = 0;
                new_start > tpb ? new_start = tpb;
                setStart(ylock*listlength+i, new_start);
                i == xlock ? start_adj_all_value = new_start;
              );
              i+=1;
            );
          );
        );
      );
    );
    
    // Alt + Left click
    (mouse_cap==1|16) ? ( 
        x = (((mouse_x - pkw)/(gfx_w - pkw))*listlength)|0;
        x >= 0 && x < listlength && y >= 0 && y < numnotes ? (
          mstate==0 ? ( // Click mouse button down
            sd_edit_num = getSubdivNum(y*listlength+x);
            !sd_edit_num ? ( // not subdiv note
              tie_erase=0;
              ylock = y;
              mask = 2^y;
              nlm=(notelist[x] & mask); // nlm != 0 if note exists in clicked location
              mstate = nlm ? ms_notetie;
              nlm ? (
                (notetie[x] & mask) ? (
                  notetie[x]-=mask; // Erase note tie
                  tie_erase=1;
                ) : (
                  x < listlength-1 && !getSubdivNum(y*listlength+x+1) ? ( // Don't add tie to the last note of sequence or before subdiv
                    notetie[x]+=mask; // Add note tie
                    !(notelist[x+1] & mask) ? ( // No note in next square
                      notelist[x+1]+=mask; // Add note to next square
                      setVelo(y*listlength+x+1, getVelo(y*listlength+x)); // using same velocity
                    ); 
                  );
                );
              );
            );
          ) : ( // Keeping mouse button down / dragging
            lcnt = abs(last_x-x)|0;
            lcnt > 0 ? (
              dx = (x-last_x)/lcnt;
            );
            lcnt >= 1 ? loop(lcnt,
              last_x += dx;
              mask = 2^(ylock|0);
              last_x_int=(last_x|0);
              nlm=(notelist[last_x_int] & mask);
              nlm && !getSubdivNum(ylock*listlength+last_x_int) ? ( //Drag started on top of note and is currently on top of note and not on top of subdiv
                !(notetie[last_x_int] & mask) ? ( // No note tie in current note
                  !tie_erase ? ( // Drag was started by adding tie, so we continue adding ties
                    last_x_int < listlength-1 && !getSubdivNum(ylock*listlength+last_x_int+1) ? ( // Don't add tie to the last note of sequence or before subdiv
                      notetie[last_x_int]+=mask; // Add note tie
                      !(notelist[last_x_int+1] & mask) ? ( // No note in next square
                        notelist[last_x_int+1]+=mask; // Add note to next square
                        setVelo(ylock*listlength+last_x_int+1, getVelo(ylock*listlength+last_x_int)); // using same velocity
                      );
                    );
                  );
                ) : ( // Note tie already exists in current note
                  tie_erase ? ( // Drag was started by erasing tie, so we continue erasing ties
                    notetie[last_x_int]-=mask; // Erase note tie
                  );
                );        
              );
            );
          );
          last_x = x;
        );
    ); // Mouse button released
    
    // Shift + Right click
    mouse_cap==(2|8) ? ( 
      x = (((mouse_x - pkw)/(gfx_w - pkw))*listlength)|0;
      mstate==0 ? (
        x >= 0 && x < listlength ? (
          ylock = y;
          xlock = x;
          mask = 2^y;
          nlm=(notelist[x] & mask);
          nlm_orig=nlm;
          mstate = ms_subdivnum;
          morig = mouse_y;
          sd_orig = getSubdivNum(ylock*listlength+x);
          draglen=ldraglen=0;
          note_enabled_for_sd=0;
          sd_set_to_disabled=0;
        );
      ) : (
        draglen=((morig - mouse_y)/10)|0;
        ldraglen != draglen ? (
          ldraglen = draglen;
          mask=2^ylock;
          sd_new = sd_orig+draglen;
          sd_new < 0 ? sd_new = 0;
          sd_new > 7 ? sd_new = 7;
          !nlm && sd_new > 0 ? (notelist[xlock]+=mask; nlm=(notelist[xlock] & mask); note_enabled_for_sd=1; );
          setSubdivNum(ylock*listlength+xlock, sd_new);
          nlm_orig && (sd_orig == 0 || sd_set_to_disabled) && sd_new > 0 ? (
            setSubdivVelo(ylock, xlock, 0, getVelo(ylock*listlength+xlock));
            setSubdivNotelist(ylock, xlock, 1);
            sd_set_to_disabled=0;
            (notetie[xlock] & mask) ? notetie[xlock]-=mask; // Erase tie from current 
            xlock > 0 && (notetie[xlock-1] & mask) ? notetie[xlock-1]-=mask; // Erase tie from previous 
          );
          sd_new == 0 ? (
            getSubdivNotelist(ylock, xlock)&1 ? (
              setVelo(ylock*listlength+xlock, getSubdivVelo(ylock, xlock, 0));
              setSubdivNotelist(ylock, xlock, 0);
              sd_set_to_disabled=1;
            ) : (
              (sd_orig || note_enabled_for_sd) && !sd_set_to_disabled ? (
                nlm ? (notelist[xlock]-=mask; nlm=0;);
              );
            );
          );
        );
      );
    );
    
  ) : !(mstate&ms_grid) ? ( // Mouse within piano roll keys

    // Left click
    (mouse_cap==(1) && mode == 1) ? ( 
        mstate==0 ? ( // Click / Button down
          mstate=ms_rowtranpose;
          mouse_x < chw ? ms_sub_channelchange=1;
          nt_note=y;
          mstart = mouse_y;
          nt_trans_start = notetranspose[nt_note];
          nt_channel_start = rowchannel[nt_note];
        ) : ( // Drag
          draglen=((mstart - mouse_y)/10)|0;

          ldraglen != draglen ? (
            ldraglen = draglen;
            prev_nt = basenote+nt_note+notetranspose[nt_note];
            want_noteoff_nt=1;
            ms_sub_channelchange ? ( // Change row MIDI channel
              new_chan = nt_channel_start + draglen;
              new_chan < 0 ? new_chan = 16 - (new_chan % 16) : new_chan = new_chan % 16;
              rowchannel[nt_note]= new_chan;
            ) : ( // Change row transpose
              is_midi_val(basenote+nt_note+nt_trans_start+draglen) ? (
                notetranspose[nt_note]=nt_trans_start + draglen;
              );
            );
            want_preview_nt = nt_note;
          
            // Clear old note name
            ypos = tbh+(numnotes-1-nt_note)*row_height;
            color(7);
            gfx_x=0.0; gfx_y=ypos;
            gfx_rectto(pkw+8, ypos+row_height); // +8 because negative octave numbers will overlap with note squares     
            // Update note name
            draw_drum_note(nt_note);
            // Draw new highlight
            draglen == 0 ? (
              color(2);
            ) : (
              color(17);
            );
            gfx_x=2.0; gfx_y=ypos+1;
            gfx_rectto(pkw-3, ypos+row_height-1);
          );
          
      );
    ) : (
      want_previewoff_nt=1;
      ms_sub_channelchange=0;
    );
    
    // Shift + Left click on piano roll keys
    (mouse_cap==(1|8)) ? (
      mstate==0 ? ( // Click / Button down
        mstate=ms_duplseq;
        listlength*2 <= max_seqlength ? (
          change_seqlen(listlength*2);
          sliderchange(slider3=listlength);
          memcpy(notelist+listlength/2, notelist, listlength/2); // Duplicate notes
          memcpy(notetie+listlength/2, notetie, listlength/2); // Duplicate notetie
          // Duplicate other note properties (velocity, start offset, subdivided notes)
          y=0;
          loop(max_numnotes,
            memcpy(velolist+y*listlength+listlength/2, velolist+y*listlength, listlength/2);
            memcpy(sdlist+sdlist_p+y*max_seqlength*max_subdivs+(listlength/2)*max_subdivs, sdlist+sdlist_p+y*max_seqlength*max_subdivs, (listlength/2)*max_subdivs);
            y+=1;
          );
          memcpy(cclist+listlength/2, cclist, listlength/2); // Duplicate envelopes
        );
    
      );
    refresh_tb=1;
    );
    
    // Ctrl + Alt + Right click on piano roll keys
    (mouse_cap==(2|4|16)) ? (
      mstate==0 ? ( // Click / Button down
        mstate=ms_doublespbpreservepos;
        listlength*2 <= max_seqlength && steps_per_beat*2 <= max_steps_per_beat ? (
          change_seqlen(listlength*2);
          sliderchange(slider3=listlength);
          steps_per_beat=steps_per_beat*2;
          sliderchange(slider9=steps_per_beat);
          steps_per_beat_pat[p]=steps_per_beat;
          rateadj = steps_per_beat * rate;
          p <= chain ? update_chain_beats();
          x=listlength/2-1;
          loop(listlength/2-1,
            notelist[2*x]=notelist[x]; notelist[x]=0; // Move notes
            notelist[2*x+1]=0;
            notetie[2*x]=notetie[x]; notetie[x]=0; // Move noteties
            notetie[2*x+1]=0;
            // Move other note properties (velocity, start offset, subdivided notes)
            y=0;
            loop(max_numnotes,
              velolist[y*listlength+2*x]=velolist[y*listlength+x]; velolist[y*listlength+x]=0;
              velolist[y*listlength+2*x+1]=0;
              memcpy(sdlist+sdlist_p+y*max_seqlength*max_subdivs+2*x*max_subdivs, sdlist+sdlist_p+y*max_seqlength*max_subdivs+x*max_subdivs, max_subdivs);
              memset(sdlist+sdlist_p+y*max_seqlength*max_subdivs+x*max_subdivs, 0, max_subdivs);
              memset(sdlist+sdlist_p+y*max_seqlength*max_subdivs+2*x*max_subdivs+1, 0, max_subdivs);
              y+=1;
            );
            cclist[2*x]=cclist[x]; cclist[x]=0; // Move envelopes
            cclist[2*x+1]=0;
            x-=1;
          );
        );
      );
    refresh_tb=1;
    );
    
    // Ctrl + Alt + Left click on piano roll keys
    (mouse_cap==(1|4|16)) ? (
      mstate==0 ? ( // Click / Button down
        mstate=ms_halvespbpreservepos;
        half_listlength=(listlength/2)|0;
        half_steps_per_beat=(steps_per_beat/2)|0;
        half_listlength >= 4 && half_steps_per_beat >= 1 ? (
          x=1;
          loop(half_listlength-1,
            notelist[x]=notelist[2*x]; // Move notes
            notetie[x]=notetie[2*x]; // Move noteties
            // Move other note properties (velocity, start offset, subdivided notes)
            y=0;
            loop(max_numnotes,
              velolist[y*listlength+x]=velolist[y*listlength+2*x];
              memcpy(sdlist+sdlist_p+y*max_seqlength*max_subdivs+x*max_subdivs, sdlist+sdlist_p+y*max_seqlength*max_subdivs+2*x*max_subdivs, max_subdivs);
              y+=1;
            );
            cclist[x]=cclist[2*x]; // Move envelopes
            x+=1;
          );
          // Clear the second half that will be outside sequence length
          memset(notelist+half_listlength, 0, listlength-half_listlength);
          memset(notetie+half_listlength, 0, listlength-half_listlength);
          y=0;
          loop(max_numnotes,
            memset(velolist+y*listlength+half_listlength, 0, listlength-half_listlength);
            memset(sdlist+sdlist_p+y*max_seqlength*max_subdivs+half_listlength*max_subdivs, 0, (listlength-half_listlength)*max_subdivs);
            y+=1;
          );
          memset(cclist+half_listlength, 0, listlength-half_listlength);
          change_seqlen(half_listlength);
          sliderchange(slider3=listlength);
          steps_per_beat=half_steps_per_beat;
          sliderchange(slider9=steps_per_beat);
          steps_per_beat_pat[p]=steps_per_beat;
          rateadj = steps_per_beat * rate;
          p <= chain ? update_chain_beats();
        );
      );
    refresh_tb=1;
    );
    
    // Ctrl + Left click on piano roll keys
    (mouse_cap==(1|4)) ? (
      mstate==0 ? ( // Click / Button down
        mstate=ms_halvespb;
        half_steps_per_beat=(steps_per_beat/2)|0;
        half_steps_per_beat >= 1 ? (
          steps_per_beat=half_steps_per_beat;
          sliderchange(slider9=steps_per_beat);
          steps_per_beat_pat[p]=steps_per_beat;
          rateadj = steps_per_beat * rate;
          p <= chain ? update_chain_beats();
        );
      );
    refresh_tb=1;
    );

    // Ctrl + Right click on piano roll keys
    (mouse_cap==(2|4)) ? (
      mstate==0 ? ( // Click / Button down
        mstate=ms_doublespb;
        steps_per_beat*2 <= max_steps_per_beat ? (
          steps_per_beat=steps_per_beat*2;
          sliderchange(slider9=steps_per_beat);
          steps_per_beat_pat[p]=steps_per_beat;
          rateadj = steps_per_beat * rate;
          p <= chain ? update_chain_beats();
        );
      );
    refresh_tb=1;
    );
    
    // Shift + Left click on piano roll keys
    (mouse_cap==(1|8)) ? (
      mstate==0 ? ( // Click / Button down
        mstate=ms_duplseq;
        listlength*2 <= max_seqlength ? (
          change_seqlen(listlength*2);
          sliderchange(slider3=listlength);
          memcpy(notelist+listlength/2, notelist, listlength/2); // Duplicate notes
          memcpy(notetie+listlength/2, notetie, listlength/2); // Duplicate notetie
          // Duplicate other note properties (velocity, start offset, subdivided notes)
          y=0;
          loop(max_numnotes,
            memcpy(velolist+y*listlength+listlength/2, velolist+y*listlength, listlength/2);
            memcpy(sdlist+sdlist_p+y*max_seqlength*max_subdivs+(listlength/2)*max_subdivs, sdlist+sdlist_p+y*max_seqlength*max_subdivs, (listlength/2)*max_subdivs);
            y+=1;
          );
          memcpy(cclist+listlength/2, cclist, listlength/2); // Duplicate envelopes
        );
      );
    );
  );

) : (
mouse_x > 0 && mstate==0 && ((mouse_y > tbrh && mouse_y < 2*tbrh && mouse_x < 28*busp) || (mouse_y > 2*tbrh && mouse_y < 3*tbrh && mouse_x < 24*busp)) || (mstate&ms_tb_r2) || (mstate&ms_tb_r3)) ? ( // Mouse on pattern buttons
 
  button = (mouse_x / busp)|0;
  mouse_x > busp * 24-1 && mouse_y < 2*tbrh ? (button=49;);
  mouse_y > 2*tbrh ? (button+=24;);
   
  button >= 0 && button <= 48 ? (
    hl_state=hl_pattern+button;
  ) : (
    button !=49 ? hl_state=0;
  );

    (button == 49 && mstate==0) ? (
    // Change colors
    (mouse_cap==1) || (mouse_cap==2) ? (
      mstate==0 ? (
        // Click / Button down
        mstate=ms_tb_colorbutt;
        (mouse_cap==1) ? (
          colorscheme+=1;
          colorscheme>ncolorschemes ? (colorscheme=1;);
        ):(
          colorscheme-=1;
          colorscheme<1 ? (colorscheme=ncolorschemes;);
        );
        sliderchange(slider43=colorscheme);
        refresh_tb=1;
        lbasenote = -1;
      );
    );
  mstate==0 ? hl_state=hl_colorbutt;
  );
  
  // Left click
  (mouse_cap==1) ? (
    mstate==0 ? ( // Click / Button down
      
      button < 48 ? (
	    mstate=ms_tb_patchange;
        change_pattern(button+(pat_shift*48)); // Change active pattern
        sliderchange(slider1=p);
        sliderchange(slider3=listlength);
      );
    );
  );
  
  // Ctrl + Left click
  (mouse_cap==1|4) ? (
    mstate==0 ? ( // Click / Button down
      mstate=ms_tb_patcopy;
      (button < 48) && (button+(pat_shift*48)) != p ? (
        source_p = p;
        source_length = listlength;
        s_sdlist = sdlist_p;
        steps_per_beat_pat[button+(pat_shift*48)]=steps_per_beat_pat[source_p]; // set steps_per_beat of the target pattern
        change_pattern(button+(pat_shift*48)); // Change active pattern to clicked 
        listlength != source_length ?(
          change_seqlen(source_length);
        );
        p <= chain ? update_chain_beats();
        s_notelist=0;
        s_velolist=velolist_base;
        i=0;
        loop(source_p,
          s_notelist+=listlength_pat[i];
          s_velolist+=max_numnotes*listlength_pat[i];
          i+=1;
        );
        s_notetie=notetie_base+s_notelist;
        s_cclist=cclist_base+s_notelist;
        s_notelist=notelist_base+s_notelist;
        // Copy content from source pattern to the target pattern 
        memcpy(notelist, s_notelist, listlength);
        memcpy(notetie, s_notetie, listlength);
        memcpy(cclist, s_cclist, listlength);
        memcpy(velolist, s_velolist, listlength*max_numnotes);
        memcpy(sdlist+sdlist_p, sdlist+s_sdlist, max_seqlength*max_numnotes*max_subdivs);
        memcpy(sparebuf + p*1024, sparebuf + source_p*1024, max_seqlength);
        memcpy(sparebuf_tie + p*1024, sparebuf_tie + source_p*1024, max_seqlength);
        memcpy(sparebuf2 + p*max_numnotes*max_seqlength, sparebuf2 + source_p*max_numnotes*max_seqlength, max_numnotes*max_seqlength);
        sliderchange(slider1=p);
        sliderchange(slider3=listlength);
        sliderchange(slider9=steps_per_beat);
        
      );
    );
  );
  
  // Alt + Left click
  (mouse_cap==1|16) ? (
    mstate==0 ? ( // Click / Button down
      mstate=ms_tb_chain;
      button < npatterns ? (
      (chain==(button+(pat_shift*48)))?(
          chain=0;
      sliderchange(slider12=chain);
      refresh_tb=1;
      update_chain_beats();
      ):(
          chain=button+(pat_shift*48);
          sliderchange(slider12=chain);
      refresh_tb=1;
      update_chain_beats();
        );
    );
    );
  );
  
  // Ctrl + Right click
  (mouse_cap==2|4) ? (
    mstate==0 ? ( // Click / Button down
      mstate=ms_tb_patclear;
      button < npatterns ? (
        clear_pattern(button+(pat_shift*48));
        update_has_notes();
        refresh_tb=1;
      );
    );
  );
) : (mouse_y > 3*tbrh && mouse_y < 4*tbrh && mouse_x > 0 && mstate==0) || (mstate&ms_tb_r4) ? ( // Mouse on fourth toolbar row

  button = (mouse_x / busp)|0;
  
  button == 0 ? (
    (mouse_cap==1) ? (
      mstate==0 ? ( // Click / Button down 
        patternmode==0?(
        xtrapatts==0? (// initialize extra 48 patterns when first needed - save memory
        memset(sparebuf,      0, npatterns*1024);
        memset(sparebuf_tie,  0, npatterns*1024);
        memset(sparebuf_cc,   0, npatterns*1024);
        memset(sparebuf2,     0, npatterns*max_seqlength*max_numnotes);oldpat=p;
        npatterns=96; sliderchange(slider47=npatterns);
        notelist=0;
        velolist=velolist_base;
        i=0;
        loop(48,
          notelist+=listlength_pat[i];
          velolist+=max_numnotes*listlength_pat[i];
          i+=1;
        );
        notetie=notetie_base+notelist;
        cclist=cclist_base+notelist;
        notelist=notelist_base+notelist;
        sdlist_p=49*max_numnotes*max_seqlength*max_subdivs;  
        memset(notelist, 0, 3072);
        memset(notetie,  0, 3072);
        memset(cclist,   0, 3072); 
        memset(velolist, 0, 3072*max_numnotes);   
        memset(sdlist_p, 0, 3072*max_numnotes*max_subdivs);
        memset(listlength_pat+48, 16, 48); 
        memset(steps_per_beat_pat+48, 4, 48); 
        listlength_all+=768;
        update_has_notes();
        refresh_tb=1;
        xtrapatts=1;
        sliderchange(slider46=xtrapatts);
        change_pattern(oldpat);
        );
        mstate=ms_tb_patshift;
        pat_shift = !pat_shift; // show next 48 patts
        sliderchange(slider44=pat_shift);
        refresh_tb=1;
        );
      );
    );
  mstate==0 ? hl_state=hl_patshift;
  ) : button == 1 ? (
    // Left click play_before_start button
    (mouse_cap==1) ? (
      mstate==0 ? ( // Click / Button down
        mstate=ms_tb_button_noundo;
    //mstate=ms_tb_patshift;
        play_before_start = !play_before_start; // toggle play_before_start state
        sliderchange(slider41=play_before_start);
        refresh_tb=1;
      );
    );
    mstate==0 ? hl_state=hl_playbeforestart;

  ) : button == 2 || button == 3 ? (
    // Left click Start beat position display
    (mouse_cap==1) || (mouse_cap==2) ? (
      mstate==0 ? ( // Click / Button down
        ((mouse_cap==1) && beat_position != start_beatpos)||((mouse_cap==2) && start_beatpos != 0) ? (
          mstate=ms_tb_button;
          (mouse_cap==1) ? (
            beat_position >= -99 && beat_position <= 9999 ? (
              start_beatpos = beat_position; // set start beat position to current beat_position
            );
          ) : start_beatpos = 0; // reset to default
          sliderchange(slider40=start_beatpos);
          refresh_tb=1;
        ) : (
          mstate=ms_tb_button_noundo;
        );
      );
    );
    mstate==0 ? hl_state=hl_startpos;
  ) : button == 4 || button == 5 ? (
    // Left click Start beat position display
    (mouse_cap==1) || (mouse_cap==2) ? (
      mstate==0 ? ( // Click / Button down
        ((mouse_cap==1) && beat_position != end_beatpos)||((mouse_cap==2) && end_beatpos != -99) ? (
          mstate=ms_tb_button;
          (mouse_cap==1) ? (
            beat_position >= -99 && beat_position <= 9999 ? (
              beat_position <= start_beatpos ? (
                end_beatpos = -99; // -99 means "end position disabled" or play infinitely
              ) : (
                end_beatpos = beat_position; // set start beat position to current beat_position
              );
            );
          ) : end_beatpos = -99; // reset to default
          sliderchange(slider42=end_beatpos);
          refresh_tb=1;
        ) : (
          mstate=ms_tb_button_noundo;
        );
      );
    );
    mstate==0 ? hl_state=hl_endpos;
  ); 
  
    (button >= 6 && button <= 11 && mstate==0) || mstate==ms_tb_swing ? (
    // Left click Swing slider
	(mouse_cap==1)||(mouse_cap==2) ? (
      (mouse_cap==1) ? (
        zeropos=busp*6+1;
        x=(100*((mouse_x-zeropos)/(busp*12-2-zeropos)))|0;
        mstate==0 ? ( // Click / Button down
          mstate=ms_tb_swing;
          swing_p=x;
        ) : (
          x < 0 ? x=0;
          x > 100 ? x=100;
          swing_p=x;
        );
      );
     (mouse_cap==2) ? (   
        mstate==0 ? ( // Click / Button down
          mstate=ms_tb_swing ;
		  swing_p==100 ? (swing_p=0;) : (swing_p>=75 ? (swing_p=100;) : (swing_p>=50 ? (swing_p=75;
		  ) : (swing_p>=25 ? (swing_p=50;) : (swing_p=25;)))))); 
	  swing = ((tpb/2)*(swing_p/100))|0;
      sliderchange(slider8=swing_p);
      refresh_tb=1;
    );
    mstate==0 ? hl_state=hl_swing;
  );
  
  (button >= 18 && button <= 23 && mstate==0) ? (
    // Midi Mode
    (mouse_cap==1) || (mouse_cap==2) ? (
      mstate==0 ? (
        // Click / Button down
        mstate=ms_tb_midimode;
        (mouse_cap==1) ? (
		  midi_trigger+=1; midi_trigger>6?(midi_trigger=0;);
        ):(
          midi_trigger-=1; midi_trigger<0?(midi_trigger=6;);
        );
        sliderchange(slider10=midi_trigger);
        refresh_tb=1;
      );
    );
  mstate==0 ? hl_state=hl_midimode;
  );
  
  (button >= 12 && button <= 17 && mstate==0) || mstate==ms_tb_rate ? (
    // Change Rate 
    (mouse_cap==1)|| (mouse_cap==2) ? (
	  (mouse_cap==1) ? (
        zeropos=busp*12+1;
        x=(16*((mouse_x-zeropos)/(busp*18-2-zeropos)))|0;
        x=x/4;
        mstate==0 ? ( // Click / Button down
          mstate=ms_tb_rate;
          rate=x;
        ) : (
          x < 0.125 ? x=0.125;
          x > 4 ? x=4;
          rate=x;
        );
      );
	
	(mouse_cap==2) ? (   
      mstate==0 ? ( // Click / Button down
        mstate=ms_tb_notelen;
		rate==4 ? (rate=0.25;) : (rate>=2 ? (rate=4;) : (rate>=1 ? (rate=2;) : (rate>=0.5 ? (rate=1;
		) : (rate>=0.25 ? (rate=0.5;) : (rate=0.25;))))))); 
	  sliderchange(slider5=rate);
      steps_per_beat_pat[p]=steps_per_beat;
      rateadj = steps_per_beat * rate;
      p <= chain ? update_chain_beats();
      refresh_tb=1;
	);
  mstate==0 ? hl_state=hl_rate;
  );
  
) : (mouse_y > 4*tbrh && mouse_y < 5*tbrh && mouse_x > 0 && mstate==0) || (mstate&ms_tb_r5) ? ( // Mouse on fifth toolbar row

  button = (mouse_x / busp)|0;
  
  button == 0 ? (
    // Mode button
	(mouse_cap==1) ? (
      mstate==0 ? ( 
        mstate=ms_tb_button_noundo;
        mode = !mode; // toggle Mode state
        mode == 0 ? (
          chw=0;
          pianow=3*8+8;
        ) : (
          chw = 2*8+8;
          pianow=max_notename_length*8+8;
        );
        pkw=chw+pianow;
        sliderchange(slider7=mode);
        refresh_tb=1;
      );
    );
    mstate==0 ? hl_state=hl_mode;
  ); 
  
  button == 1 ? (
    // Left click Panic button
    (mouse_cap==1) ? (
      mstate==0 ? ( // Click / Button down
        mstate=ms_tb_panic;
        panic_go = 1;
      );
    );
    mstate==0 ? hl_state=hl_panic;
  ); 
  
  (button >= 2 && button <= 3 && mstate==0) || mstate==ms_tb_velo ? (
    // Change Velo
    (mouse_cap==1)|| (mouse_cap==2) ? (
	(mouse_cap==1) ? (
      zeropos=busp*2+1;
      x=(128*((mouse_x-zeropos)/(busp*4-2-zeropos)))|0;
      mstate==0 ? ( // Click / Button down
        mstate=ms_tb_velo;
        lastvelo=x;
      ) : (
        x < 0 ? x=0;
        x > 127 ? x=127;
        lastvelo=x;
      );
    );
	
  (mouse_cap==2) ? (   
    mstate==0 ? ( // Click / Button down
      mstate=ms_tb_notelen;
      lastvelo==127 ? (lastvelo=32;) : (lastvelo>=100 ? (lastvelo=127;) : (lastvelo>=64 ? (lastvelo=100;
	  ) : (lastvelo>=32 ? (lastvelo=64;) : (lastvelo=32;)))))); 
	 // sliderchange(slider5=rate);
    refresh_tb=1;
	);
  mstate==0 ? hl_state=hl_velo;
  );
  
  (button >=4 && button <= 5 && mstate==0) || mstate==ms_tb_move ? ( 
  (mouse_cap==1||mouse_cap==1|4||mouse_cap==1|4|16||mouse_cap==1|16) ? ( 
  //LEFT CLICK // --------------------- MOVE Right
      mstate==0 ? (
      (mouse_cap==1|4|16||mouse_cap==1|16) ? (z=0;):(z=1); //cc's move?
        (mouse_cap==1|4||mouse_cap==1|4|16) ? (mstate=ms_tb_move;); //scroll or single click
    moveRightLeft(1,z);
      );
    );
  (mouse_cap==2||mouse_cap==2|4||mouse_cap==2|4|16||mouse_cap==2|16) ? ( 
  //RIGHT CLICK // -------------------- MOVE Left
      mstate==0 ? ( 
    (mouse_cap==2|4|16||mouse_cap==2|16) ? (z=0;):(z=1); //cc's move?
        (mouse_cap==2|4||mouse_cap==2|4|16) ? (mstate=ms_tb_move;);
        moveRightLeft(0,z);
      );
    );
    (mouse_cap==1|8||mouse_cap==1|4|8) ? ( 
  //LEFT CLICK // ------------------ MOVE Up
      mstate==0 ? ( 
        (mouse_cap==1|4|8) ? (mstate=ms_tb_move;);
    moveUpDown(1);
      );
    );
  (mouse_cap==2|8||mouse_cap==2|4|8) ? (
  //RIGHT CLICK // ----------------- MOVE Down
      mstate==0 ? ( 
        (mouse_cap==2|4|8) ? (mstate=ms_tb_move;);
        moveUpDown(0);
      );
    );
  mstate==0 ? (hl_state=hl_move;);
  ); 

  (button >= 6 && button <= 11 && mstate==0) || mstate==ms_tb_notelen ? (
    // Left click Note length slider
    (mouse_cap==1) || (mouse_cap==2) ? (
	  (mouse_cap==1) ? (
        zeropos=busp*6+1;
        x=(100*((mouse_x-zeropos)/(busp*12-2-zeropos)))|0;
        mstate==0 ? ( // Click / Button down
          mstate=ms_tb_notelen;
          notelen_p=x;
          defnotelen = (tpb*(notelen_p/100))|0;
          sliderchange(slider6=notelen_p);
          refresh_tb=1;
        ) : (
          x < 0 ? x=0;
          x > 100 ? x=100;
          notelen_p=x;
          defnotelen = (tpb*(notelen_p/100))|0;
          sliderchange(slider6=notelen_p);
          refresh_tb=1;
        );
      );
	
      (mouse_cap==2) ? (   
        mstate==0 ? ( // Click / Button down
        mstate=ms_tb_notelen;
        notelen_p==100 ? (notelen_p=10;) : (notelen_p>=75 ? (notelen_p=100;) : (notelen_p>=50 ? (notelen_p=75;
	    ) : (notelen_p>=25 ? (notelen_p=50;) : (notelen_p=25;)))))
	  ); 
      defnotelen = (tpb*(notelen_p/100))|0;
      sliderchange(slider6=notelen_p);
      refresh_tb=1;
    );

    mstate==0 ? hl_state=hl_notelen;
  );
  
  (button >= 12 && button <= 17 && mstate==0) || mstate==ms_tb_patternlen ? (
    // Left click Pattern Length slider
   (mouse_cap==1) || (mouse_cap==2)? (
   (mouse_cap==1) ? (
      zeropos=busp*12+1;
      x=1+(max_seqlength*((mouse_x-zeropos)/(busp*18-2-zeropos)))|0;
      mstate==0 ? ( // Click / Button down
        mstate=ms_tb_patternlen;
        listlength=x;
      ) : (
        x < 2 ? x=2;
        x > max_seqlength ? x=max_seqlength;
        listlength=x;
      );
    );
	
	(mouse_cap==2) ? (   
      mstate==0 ? ( // Click / Button down
        mstate=ms_tb_patternlen;
		listlength==max_seqlength ? (
		listlength=8;) : (listlength>=64 ? (listlength=128;) : (listlength>=32 ? (listlength=64;
		) : (listlength>=16 ? (listlength=32;) : (listlength>=8 ? (listlength=16;) : (listlength=8;)))))
	  )
	); 
	sliderchange(slider3=listlength);
    change_seqlen(listlength);
    steps_per_beat=slider9|0;
    steps_per_beat_pat[p]=steps_per_beat;
    rateadj = steps_per_beat * rate;
    p <= chain ? update_chain_beats();
    refresh_tb=1;
	);
    mstate==0 ? hl_state=hl_patternlen;
  );
  
  (button >= 18 && button <= 23 && mstate==0) || mstate==ms_tb_numbeats ? (
    // Left click Steps per Beat slider
    (mouse_cap==1)||(mouse_cap==2)  ? (
	  (mouse_cap==1) ? (
        zeropos=busp*18+1;
        x=1+(15*((mouse_x-zeropos)/(busp*24-2-zeropos)))|0;
        mstate==0 ? ( // Click / Button down
          mstate=ms_tb_numbeats;
          steps_per_beat=x;
          refresh_tb=1;
        ) : (
          x < 1 ? x=1;
          x > 16 ? x=16;
         steps_per_beat=x;
        );
      );
     (mouse_cap==2) ? (   
          mstate==0 ? ( // Click / Button down
            mstate=ms_tb_notelen;
		    steps_per_beat==16 ? (steps_per_beat=2;) : (steps_per_beat>=8 ? (steps_per_beat=16;
			) : (steps_per_beat>=4 ? (steps_per_beat=8;) : (steps_per_beat>=2 ? (steps_per_beat=4;
			) : (steps_per_beat=2;)))))  
	  ); 
      sliderchange(slider9=steps_per_beat);
      steps_per_beat_pat[p]=steps_per_beat;
      rateadj = steps_per_beat * rate;
      p <= chain ? update_chain_beats();
      refresh_tb=1;
	  );
    mstate==0 ? hl_state=hl_numbeats;
  );
  
  (button >= 24 && button <= 27 && mstate==0) ? (
    // Settings section
    (mouse_cap==1) || (mouse_cap==2) ? (
      mstate==0 ? (
        // Click / Button down
        mstate=ms_tb_settings;
        (mouse_cap==1) ? (
	  settingsopen==0 ? (
          tbh+=1*tbrh; // toolbar height
	  settingsopen=1;
        ):(
	  tbh-=1*tbrh; // toolbar height
	  settingsopen=0;
	); 
	);
        refresh_tb=1;
        lbasenote = -1;
      );
    );
  mstate==0 ? hl_state=hl_settings;
  );
  
) : (mouse_y > 0 && mouse_y < tbrh && mouse_x > 0 && mstate==0) || (mstate&ms_tb_r1) ? ( // Mouse on first toolbar row 
  x=(128*(mouse_x / tb_pianokey_end))|0;
  (x >= basenote && x <= basenote+numnotes && mstate==0) || mstate==ms_tb_pianokey_base || mstate==ms_tb_pianokey_numnotes? (
    (mouse_cap==1) ? ( // Left click toolbar pianokeys on grid note range
      mstate==0 ? ( // Click / Button down
        mstate=ms_tb_pianokey_base;
        last_x=x;
      ) : (
        x != last_x ? (
          new_note=basenote+(x - last_x);
          new_note >= 0 && new_note <= 127 ? last_x=x;
          new_note < 0 ? new_note=0;
          new_note > 127 ? new_note=127;
          basenote=new_note;
          sliderchange(slider2=basenote);
          refresh_tb=1;
        );
      );
    );
    (mouse_cap==2) ? ( // Right click toolbar pianokeys on grid note range
      mstate==0 ? ( // Click / Button down
        mstate=ms_tb_pianokey_numnotes;
        last_x=x;
      ) : (
        x != last_x ? (
          new_note=numnotes+(x - last_x);
          new_note >= 1 && new_note <= max_numnotes ? last_x=x;
          new_note < 1 ? new_note=1;
          new_note > max_numnotes ? new_note=max_numnotes;
          numnotes=new_note;
          sliderchange(slider4=numnotes);
          refresh_tb=1;
        );
      );
    );
  ) : (x >= trig_note_start && x <= trig_note_start+numnotes && mstate==0) || mstate==ms_tb_pianokey_trig ? (
    // Left click toolbar pianokeys on trigger note range
    (mouse_cap==1) ? ( 
      mstate==0 ? ( // Click / Button down
        mstate=ms_tb_pianokey_trig;
        last_x=x;
      ) : (
        x != last_x ? (
          new_note=trig_note_start+(x - last_x);
          new_note >= 0 && new_note <= 127 ? last_x=x;
          new_note < 0 ? new_note=0;
          new_note > 127 ? new_note=127;
          trig_note_start=new_note;
          sliderchange(slider11=trig_note_start);
          refresh_tb=1;
        );
      );
    );
  );
) : (mouse_y > grid_bottom && mouse_y < gfx_h && mouse_x > 0 && mstate==0) || (mstate&ms_el) ? ( // Mouse on envelope lane

  ((mouse_y < grid_bottom+el_divh && mstate==0) || mstate==ms_el_divider) ? (
    // Left click
    (mouse_cap==1) ? (
      mstate==0 ? ( // Click / Button down
        mstate=ms_el_divider;
        morig = mouse_y;
        dmouse_y = morig-grid_bottom;
        
      ) : (
        mouse_y != morig ? (
          (mouse_y-dmouse_y > tbh) ? (
            (mouse_y+(el_divh-dmouse_y) <= gfx_h) ? (
              elh_percent = (gfx_h-tbh-(mouse_y-tbh-dmouse_y))/(gfx_h-tbh);
            ) : (
              elh_percent = 0; // hit to bottom
            );
          ) : (
            elh_percent = 1; // hit to top
          );
          sliderchange(slider13=elh_percent);
          recalc_elh=1;
        );
      );
    );
  );
  
  ((mouse_x > pkw && mouse_y >= grid_bottom+el_divh && mstate==0) || mstate==ms_el_envdraw || mstate==ms_el_enverase) ? (
    // Left click
    (mouse_cap==1) ? (
      x = (((mouse_x - pkw)/(gfx_w - pkw))*listlength)|0;
      x >= 0 && x < listlength ? (
        envval = ((gfx_h - mouse_y) / env_max_h)*127|0;
        mstate==0 ? ( // Click / Button down
          mstate=ms_el_envdraw;
          cclist[x]=setCc(cclist[x], cc_to_adjust, envval | 0x80);
          lastenv_changed_x=x;
        ) : (
          lcnt = abs(last_x-x)|0;
          lcnt > 0 ? (
            dx = (x-last_x)/lcnt;
            dmoy = (mouse_y - last_mo_y)/lcnt;
          );
          lcnt >= 1 ? loop(lcnt,
            last_x += dx;
            last_mo_y += dmoy;
            envval = ((gfx_h - last_mo_y) / env_max_h)*127|0;
            envval > 127 ? envval=127;
            envval < 0 ? envval=0;
            cclist[last_x|0]=setCc(cclist[last_x|0], cc_to_adjust, envval | 0x80);
            lastenv_changed_x=last_x|0;
          ) : (
            envval > 127 ? envval=127;
            envval < 0 ? envval=0;
            cclist[x]=setCc(cclist[x], cc_to_adjust, envval | 0x80);
            lastenv_changed_x=x;
          );
        );
        last_x = x;
        last_mo_y = mouse_y;  
      );
    );
    
    // Right click
    (mouse_cap==2) ? (
      x = (((mouse_x - pkw)/(gfx_w - pkw))*listlength)|0;
      x >= 0 && x < listlength ? (
        mstate==0 ? ( // Click / Button down
          mstate=ms_el_enverase;
          cclist[x]=setCc(cclist[x], cc_to_adjust, 0);
        ) : (
          lcnt = abs(last_x-x)|0;
          lcnt > 0 ? (
            dx = (x-last_x)/lcnt;
          );
          lcnt >= 1 ? loop(lcnt,
            last_x += dx;
            cclist[last_x|0]=setCc(cclist[last_x|0], cc_to_adjust, 0);
          ) : (
            cclist[x]=setCc(cclist[x], cc_to_adjust, 0);
          );
        );
        last_x = x;
      );
    );
  );
  
  (mouse_x < pkw && mouse_y >= grid_bottom+el_divh && mstate==0) || mstate==ms_el_ctrlchange ? (
    // Left click
    (mouse_cap==1) ? ( 
      y = (((mouse_y - grid_bottom - el_divh) / (elh - el_divh))*numccs )|0;
      mstate==0 ? ( // Click / Button down
        mstate = ms_el_ctrlchange;
        mouse_x < chw ? ms_sub_channelchange=1;
        cc_to_adjust=y;
        sliderchange(slider14=cc_to_adjust);
        mstart = mouse_y;
        cc_type_start = cc_type[cc_to_adjust];
        cc_chan_start = cc_chan[cc_to_adjust];
        draw_cc_controls();
      ) : ( // Drag
        draglen=((mstart - mouse_y)/10)|0;
        ldraglen != draglen ? (
          ldraglen = draglen;
          ms_sub_channelchange==0 ? (
            new_cc = cc_type_start + draglen;
            new_cc < 0 ? new_cc = 128 - (new_cc % 128) : new_cc = new_cc % 128;
            cc_type[cc_to_adjust]=new_cc;
            cc_to_adjust == 0 ? sliderchange(slider20=cc_type[cc_to_adjust]);
            cc_to_adjust == 1 ? sliderchange(slider21=cc_type[cc_to_adjust]);
            cc_to_adjust == 2 ? sliderchange(slider22=cc_type[cc_to_adjust]);
            cc_to_adjust == 3 ? sliderchange(slider23=cc_type[cc_to_adjust]);
          ) : (
            new_cc = cc_chan_start + draglen;
            new_cc < 0 ? new_cc = 16 - (new_cc % 16) : new_cc = new_cc % 16;
            cc_chan[cc_to_adjust]=new_cc;
            cc_to_adjust == 0 ? sliderchange(slider30=cc_chan[cc_to_adjust]);
            cc_to_adjust == 1 ? sliderchange(slider31=cc_chan[cc_to_adjust]);
            cc_to_adjust == 2 ? sliderchange(slider32=cc_chan[cc_to_adjust]);
            cc_to_adjust == 3 ? sliderchange(slider33=cc_chan[cc_to_adjust]);
          );
          draw_cc_controls();
        ); 
      );
    ) : (
      ms_sub_channelchange=0; 
    );
  );
):(

(mouse_x > 0 && mstate==0 && mouse_y > 5*tbrh && mouse_y < tbh && mouse_x < 8*busp) || (mstate&ms_tb_r6)) ? ( // Mouse on 6th row
button = (mouse_x / busp)|0;
  
  button >=0 && button <=7 ? (
    // Step/Pattern Mode Button
    (mouse_cap==1) ? (
      mstate==0 ? ( // Click / Button down    
		mstate=ms_tb_pattmode;
		pm=patternmode;
		pm=!pm;
		sliderchange(slider45=pm);
		initialize48(pm);
		refresh_tb=1;
      );
    );
    mstate==0 ? hl_state=hl_pattmode;
  ); 
);

!(mouse_cap&3) ? (
  mstate ? (
    mstate != ms_el_divider && mstate != ms_preview && mstate != ms_tb_button_noundo ? (
      sliderchange(-1); // create undo point
    );
    mstate=0; // Clear all mouse states
    sd_editval_show=0;
  );
); 

(mouse_y < tbrh || mouse_y > tbh || mouse_x < 0 
 || ( mouse_x > 28*busp && (mouse_y > tbrh && mouse_y < 2*tbrh) ) 
 || ( mouse_x > 24*busp && (mouse_y > 2*tbrh && mouse_y < 3*tbrh) ) 
 || (mouse_x > 24*busp  && (mouse_y > 3*tbrh && mouse_y < 4*tbrh)) 
 || (mouse_x > 28*busp  && (mouse_y > 4*tbrh && mouse_y < 5*tbrh))
 || (mouse_x > 8*busp  && (mouse_y > 5*tbrh && mouse_y < 6*tbrh))
) && (mstate!=ms_tb_numbeats)? hl_state=0; // Clear toolbar highlights

// Erase everything within note matrix
color(7);
gfx_x=pkw; gfx_y=tbh; gfx_rectto(gfx_w,grid_bottom);

// Erase everything within envelope lane
gfx_x=pkw-1; // -1 to erase also the leftmost pixels of event dots
gfx_y=gfx_h - elh+el_divh; gfx_rectto(gfx_w,gfx_h);

refresh_cc_controls ? (
  draw_cc_controls();
  refresh_cc_controls=0;
);

// Draw octave lines
ly=tbh;
notepos=0;
loop(numnotes,
  ly += row_height;
  octavestart = (basenote+(numnotes-1-notepos)) % 12;
  octavestart == 0 ? (
    color(18);
    gfx_x=0.0; gfx_y=ly;
    gfx_lineto(gfx_w,gfx_y,0);
  );
  notepos+=1;
);

// Draw bar lines  
xpos=pkw;
bar=1;
loop(nbars,
  xpos+=barlen;
  color(19);
  gfx_x=xpos+1; gfx_y=tbh;
  gfx_lineto(gfx_x,gfx_h,0);
  bar+=1;
);

ly=tbh;
notepos=0;
loop(numnotes,

  ty=(tbh+((notepos+1)*(grid_bottom-tbh))/(numnotes))|0;

  lx=pkw;
  xpos=0;
  mask = 2^(numnotes-1-notepos);

  cidx=(p&7);
  use_r=(cidx&1) ? 0.6 : 0.2;
  use_g=(cidx&2) ? 0.6 : 0.2;
  use_b=(cidx&4) ? 0.2 : 0.6;
  cidx==3  ? use_r*=2;
  cidx==4 ? use_g*=2;

  
  loop(listlength,
    tx = (((xpos+1)*(gfx_w - pkw))/listlength)+pkw|0;
    sel=(notelist[xpos]&mask);

    // draw square
    colorscheme==1 ? (

    gfx_a=1;
    gfx_r=use_r; gfx_g=use_g; gfx_b=use_b;  
    !(xpos % steps_per_beat) ? ( gfx_g=gfx_r; gfx_r=gfx_b; gfx_b=use_g;);
    lbeatpos == xpos ? ( gfx_r=gfx_g=gfx_b=sel?0.8:0.4; ) :
       !sel ? (gfx_r*=0.55; gfx_g*=0.55; gfx_b*=0.55; );

    ):(
    color(21); //selected standard beats 
    (xpos % steps_per_beat) == 0 ? ( 
       color(20); //selected first beat 
    );
    lbeatpos == xpos ? ( // time position color
      sel?(color(22);):(color(23);); 
      ) : !sel ? ( // standard row colour
        (xpos % steps_per_beat) == 0 ? (color(24);):(color(25);)
      ); 
    );

    curnote=(numnotes-1-notepos)*listlength+xpos;
    num_sd = getSubdivNum(curnote);
    num_sd ? (
      sd_ena = getSubdivNotelist((numnotes-1-notepos), xpos);
      sd = 0;
      lx_sd=lx;
    colorscheme==1 ? (sd_r=gfx_r;sd_g=gfx_g;sd_b=gfx_b;);
      loop(num_sd+1,
        tx_sd = (((sd+1)*(tx-lx))/(num_sd+1))+lx|0;
        (sd_ena >> sd)&1 ? ( 
        // enabled sd step
        colorscheme==1 ? (
        gfx_r=sd_r; gfx_g=sd_g; gfx_b=sd_b;
        ) : (   
        (xpos % steps_per_beat) == 0 ? (
              lbeatpos == xpos  ? (color(22);):(color(20););
            ):(
              lbeatpos == xpos  ? (color(22);):(color(21););
            );
          );
      ) : ( 
    // non selected step
      colorscheme==1 ? (
      gfx_r=sd_r*0.55; gfx_g=sd_g*0.55; gfx_b=sd_b*0.55;
      ) : (         
      (xpos % steps_per_beat) == 0 ? (
        lbeatpos == xpos  ? (color(23);):(color(24););
        ):(
        lbeatpos == xpos  ? (color(23);):(color(25););
        );
    );
    );       
      gfx_x=lx_sd ;gfx_y=ly; gfx_rectto(tx_sd,ty);
      lx_sd=tx_sd+1;
      sd += 1;
    );
      ) : (
      gfx_x=lx; gfx_y=ly; gfx_rectto(tx,ty);
    );
  
    notetie[xpos]&mask ? (
      // Draw note tie
      gfx_x=tx; gfx_y=ly; gfx_rectto(tx+2,ty);
    );
    
    sel && !(notetie[xpos-1]&mask) ? ( //Note exists and is not tied to previous note.
      // Draw velocity bar
      num_sd ? (
        sd = 0;
        lx_sd=lx;
        loop(num_sd+1,
          tx_sd = (((sd+1)*(tx-lx))/(num_sd+1))+lx|0;
          (sd_ena&1) ? (
            draw_velobar(lx_sd, ty, getSubdivVelo((numnotes-1-notepos), xpos, sd), tx_sd-lx_sd, ty-ly);
          );
          sd_ena *= 0.5;
          lx_sd=tx_sd+1;
          sd += 1;
        );
      ) : (
        draw_velobar(lx+(tx-lx-velobarwidth)*(getStart(curnote)/tpb), ty, getVelo(curnote), tx-lx, ty-ly);
      );
    );
    lx=tx+2;
    xpos+=1;
  );  

  ly=ty+2;
  notepos+=1;
);
(mstate == ms_veloedit || mstate == ms_veloeditrow || mstate == ms_startoffsetedit || mstate == ms_startoffseteditrow)&& ((nlm && sd_edit_num==0) || sd_editval_show)? (
  // Draw velocity or start offset value
  color(26);
  gfx_x=(((lastnote_changed_x)*(gfx_w - pkw))/listlength)+6+pkw|0; 
  gfx_y=tbh+(numnotes-1-ylock)*row_height-10;
  gfx_y < tbh+1 ? gfx_y = tbh+1;
  mstate == ms_veloedit ? (
    sd_edit_num ? gfx_x+=sd_x*note_w/(sd_edit_num+1);
    gfx_drawnumber(velo,0);
  ) : mstate == ms_veloeditrow ? (
    sd_edit_num ? gfx_x+=sd_x*note_w/(sd_edit_num+1);
    gfx_drawnumber(lastnote_changed_velo,0);
  ) : mstate == ms_startoffsetedit ?(
    gfx_drawnumber(new_start,0);
  ) : (
    gfx_drawnumber(start_adj_all_value,0);
  );
);

// Draw CC envelopes

env_max_h=gfx_h-grid_bottom-el_divh-1;
env_half_h=(env_max_h/2)|0;

// Draw step colors to envelope background
lx=pkw;
xpos=0;
loop(listlength,
  tx = (((xpos+1)*(gfx_w - pkw))/listlength)+pkw|0;
  colorscheme==1 ? (
    gfx_r=use_r; gfx_g=use_g; gfx_b=use_b; gfx_a=1;
    (xpos % steps_per_beat) == 0 ? ( gfx_g=gfx_r; gfx_r=gfx_b; gfx_b=use_g;);
    lbeatpos == xpos ? ( gfx_r=gfx_g=gfx_b=0.4) : (gfx_r*=0.55; gfx_g*=0.55; gfx_b*=0.55; );
  ) : (
    lbeatpos == xpos ? (color(23);) : (
    (xpos % steps_per_beat) == 0 ? (
      color(24);
      x=1;
    ) : (
      color(25);
      x=1;
    );
    );
  );
  gfx_x=lx; gfx_y=gfx_h; gfx_rectto(tx,gfx_h-env_max_h);
  lx=tx+2;
  xpos+=1;
);
  
// Draw envelope
tx = (gfx_w - pkw)/listlength;
i=(cc_to_adjust+1) % numccs; // make sure that active envelope is drawn last
loop(numccs,
  gfx_x=pkw;gfx_y=gfx_h;
  x_int=gfx_x;
  x_float=gfx_x+tx;
  x_int_prev=x_int;
  x_int=x_float|0;
  cc_step=0;
  // Find first and last enabled event
  event_enabled=0;
  first_event=0;
  last_event=0;
  loop(listlength,
    val=getCc(cclist[cc_step], i);
    val & 0x80 ? ( // Enabled event
      !event_enabled ? (
        first_event=cc_step;
        event_enabled=1;
      );
      last_event=cc_step;
    );
    cc_step+=1;
  );
  event_enabled ? ( // At least one enabled event found in this pattern
    setEnvColor(i);
    cc_step=0;
    loop(listlength,
      val=getCc(cclist[cc_step], i);
      gfx_x=x_int_prev; gfx_y_mem=gfx_y;
      val & 0x80 ? ( // event enabled
        event_enabled=1;
        val_y=gfx_h-((val&0x7F)/128*env_max_h);
        i == cc_to_adjust ? (
    gfx_a=0.5;
          cc_type[i]==10 ? gfx_y=gfx_h - env_half_h : gfx_y=gfx_h; // Draw Pan (CC 10) using bipolar env
          gfx_rectto(x_int,val_y); // Fill
        );
        gfx_x=x_int_prev; gfx_y=gfx_y_mem;
  gfx_a=0.5;
        cc_step > 0 ? gfx_lineto(gfx_x,val_y,0) : gfx_y=val_y; // Vertical line
        i == cc_to_adjust ? (
          gfx_x-=1; gfx_y-=1;
          gfx_rectto(gfx_x+4,gfx_y+4); // Event dot
          gfx_x-=3; gfx_y-=3;
        );
        gfx_lineto(x_int,gfx_y,0); // Horizontal line
      ):(
        cc_step < first_event ? (
          val_y=gfx_h-((getCc(cclist[last_event], i)&0x7F)/128*env_max_h);
        );
        i == cc_to_adjust ? (
  gfx_a=0.5;
          cc_type[i]==10 ? gfx_y=gfx_h - env_half_h : gfx_y=gfx_h;
          gfx_rectto(x_int,val_y); // Fill
        );
        gfx_x=x_int_prev; gfx_y=val_y;
        gfx_lineto(x_int,gfx_y,0); // Horizontal line
      );
      x_float+=tx;
      x_int_prev=x_int;
      x_int=x_float|0;
      cc_step+=1;
    );
  );
  i=(i+1) % numccs;;
);

// Draw envelope lane divider
color(32); // top edge
gfx_x=0; gfx_y=gfx_h - elh;
gfx_lineto(gfx_w,gfx_y,0);
color(33);  // center
gfx_x=0; gfx_y+=1;
gfx_rectto(gfx_w,gfx_y+el_divh-2);
color(34);  //bottom edge
gfx_x=0;
gfx_lineto(gfx_w,gfx_y,0);

mstate == ms_el_envdraw ? (
  // Draw envelope value txt
  colorscheme < 9 ? (color(4);) :(color(20););
  gfx_x=(((lastenv_changed_x)*(gfx_w - pkw))/listlength)+6+pkw|0; 
  gfx_y=gfx_h - envval/128*env_max_h - 8;
  gfx_y < grid_bottom+el_divh+1 ? gfx_y+=10;
  gfx_drawnumber(envval,0);
);

// Draw toolbar
refresh_tb || lgfx_w_tb != gfx_w || lgfx_h_tb != gfx_h || last_hl_state!=hl_state || drawnvelo!=lastvelo ? (
  refresh_tb=0;
  lgfx_w_tb=gfx_w;
  lgfx_h_tb=gfx_h;
  last_hl_state=hl_state;
  // Clear toolbar
  gfx_x=gfx_y=0; color(31);
  gfx_rectto(gfx_w,tbh);

  // Draw pattern buttons
  pat=0;
  loop(48, draw_pat_button(pat, pat<24 ? (pat):(pat-24);, pat<24 ? (tbrh):(2*tbrh);, ((hl_state>hl_pattern-1) && (hl_state<hl_pattern+npatterns) && (hl_state-hl_pattern==pat)));
    pat+=1;
  );
  draw_basenote_display(busp*24, 2*tbrh, 0);
  draw_trigstart_display(busp*24, 3*tbrh, 0);
  draw_patshift_button(busp*0, 3*tbrh, hl_state==hl_patshift);
  draw_startmode_button(busp*1, 3*tbrh, hl_state==hl_playbeforestart);
  draw_position_display(busp*2, 3*tbrh, hl_state==hl_startpos, start_beatpos);
  draw_position_display(busp*4, 3*tbrh, hl_state==hl_endpos, end_beatpos == -99 ? -100 : end_beatpos);
  draw_swing_slider(busp*6, 3*tbrh, hl_state==hl_swing);
  draw_rate_slider(busp*12, 3*tbrh, hl_state==hl_rate);
  draw_colors_button(busp*24, 1*tbrh, hl_state==hl_colorbutt);
  draw_midimode_button(busp*18, 3*tbrh, hl_state==hl_midimode);
  draw_mode_button(busp*0, 4*tbrh, hl_state==hl_mode);
  draw_panic_button(busp*1, 4*tbrh, hl_state==hl_panic);
  draw_velocity(busp*2, 4*tbrh, hl_state==hl_velo); // separated for redraw
  draw_move_button(busp*4,4*tbrh,hl_state==hl_move);
  draw_notelen_slider(busp*6, 4*tbrh, hl_state==hl_notelen);
  draw_patternlen_slider(busp*12, 4*tbrh, hl_state==hl_patternlen);
  draw_numbeats_slider(busp*18, 4*tbrh, hl_state==hl_numbeats);
  draw_settings_button(busp*24, 4*tbrh, hl_state==hl_settings);
  settingsopen==1 ? draw_pattmode_button(busp*0, 5*tbrh, hl_state==hl_pattmode);
  draw_tb_pianokeys(busp*0, 0);
);
